Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames

DeusEx.ScriptedPawn


00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059
00060
00061
00062
00063
00064
00065
00066
00067
00068
00069
00070
00071
00072
00073
00074
00075
00076
00077
00078
00079
00080
00081
00082
00083
00084
00085
00086
00087
00088
00089
00090
00091
00092
00093
00094
00095
00096
00097
00098
00099
00100
00101
00102
00103
00104
00105
00106
00107
00108
00109
00110
00111
00112
00113
00114
00115
00116
00117
00118
00119
00120
00121
00122
00123
00124
00125
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139
00140
00141
00142
00143
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192
00193
00194
00195
00196
00197
00198
00199
00200
00201
00202
00203
00204
00205
00206
00207
00208
00209
00210
00211
00212
00213
00214
00215
00216
00217
00218
00219
00220
00221
00222
00223
00224
00225
00226
00227
00228
00229
00230
00231
00232
00233
00234
00235
00236
00237
00238
00239
00240
00241
00242
00243
00244
00245
00246
00247
00248
00249
00250
00251
00252
00253
00254
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268
00269
00270
00271
00272
00273
00274
00275
00276
00277
00278
00279
00280
00281
00282
00283
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315
00316
00317
00318
00319
00320
00321
00322
00323
00324
00325
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335
00336
00337
00338
00339
00340
00341
00342
00343
00344
00345
00346
00347
00348
00349
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461
00462
00463
00464
00465
00466
00467
00468
00469
00470
00471
00472
00473
00474
00475
00476
00477
00478
00479
00480
00481
00482
00483
00484
00485
00486
00487
00488
00489
00490
00491
00492
00493
00494
00495
00496
00497
00498
00499
00500
00501
00502
00503
00504
00505
00506
00507
00508
00509
00510
00511
00512
00513
00514
00515
00516
00517
00518
00519
00520
00521
00522
00523
00524
00525
00526
00527
00528
00529
00530
00531
00532
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547
00548
00549
00550
00551
00552
00553
00554
00555
00556
00557
00558
00559
00560
00561
00562
00563
00564
00565
00566
00567
00568
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579
00580
00581
00582
00583
00584
00585
00586
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604
00605
00606
00607
00608
00609
00610
00611
00612
00613
00614
00615
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644
00645
00646
00647
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669
00670
00671
00672
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712
00713
00714
00715
00716
00717
00718
00719
00720
00721
00722
00723
00724
00725
00726
00727
00728
00729
00730
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747
00748
00749
00750
00751
00752
00753
00754
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789
00790
00791
00792
00793
00794
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815
00816
00817
00818
00819
00820
00821
00822
00823
00824
00825
00826
00827
00828
00829
00830
00831
00832
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850
00851
00852
00853
00854
00855
00856
00857
00858
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878
00879
00880
00881
00882
00883
00884
00885
00886
00887
00888
00889
00890
00891
00892
00893
00894
00895
00896
00897
00898
00899
00900
00901
00902
00903
00904
00905
00906
00907
00908
00909
00910
00911
00912
00913
00914
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924
00925
00926
00927
00928
00929
00930
00931
00932
00933
00934
00935
00936
00937
00938
00939
00940
00941
00942
00943
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
00962
00963
00964
00965
00966
00967
00968
00969
00970
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984
00985
00986
00987
00988
00989
00990
00991
00992
00993
00994
00995
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
01014
01015
01016
01017
01018
01019
01020
01021
01022
01023
01024
01025
01026
01027
01028
01029
01030
01031
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041
01042
01043
01044
01045
01046
01047
01048
01049
01050
01051
01052
01053
01054
01055
01056
01057
01058
01059
01060
01061
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092
01093
01094
01095
01096
01097
01098
01099
01100
01101
01102
01103
01104
01105
01106
01107
01108
01109
01110
01111
01112
01113
01114
01115
01116
01117
01118
01119
01120
01121
01122
01123
01124
01125
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136
01137
01138
01139
01140
01141
01142
01143
01144
01145
01146
01147
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161
01162
01163
01164
01165
01166
01167
01168
01169
01170
01171
01172
01173
01174
01175
01176
01177
01178
01179
01180
01181
01182
01183
01184
01185
01186
01187
01188
01189
01190
01191
01192
01193
01194
01195
01196
01197
01198
01199
01200
01201
01202
01203
01204
01205
01206
01207
01208
01209
01210
01211
01212
01213
01214
01215
01216
01217
01218
01219
01220
01221
01222
01223
01224
01225
01226
01227
01228
01229
01230
01231
01232
01233
01234
01235
01236
01237
01238
01239
01240
01241
01242
01243
01244
01245
01246
01247
01248
01249
01250
01251
01252
01253
01254
01255
01256
01257
01258
01259
01260
01261
01262
01263
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273
01274
01275
01276
01277
01278
01279
01280
01281
01282
01283
01284
01285
01286
01287
01288
01289
01290
01291
01292
01293
01294
01295
01296
01297
01298
01299
01300
01301
01302
01303
01304
01305
01306
01307
01308
01309
01310
01311
01312
01313
01314
01315
01316
01317
01318
01319
01320
01321
01322
01323
01324
01325
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337
01338
01339
01340
01341
01342
01343
01344
01345
01346
01347
01348
01349
01350
01351
01352
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363
01364
01365
01366
01367
01368
01369
01370
01371
01372
01373
01374
01375
01376
01377
01378
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390
01391
01392
01393
01394
01395
01396
01397
01398
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419
01420
01421
01422
01423
01424
01425
01426
01427
01428
01429
01430
01431
01432
01433
01434
01435
01436
01437
01438
01439
01440
01441
01442
01443
01444
01445
01446
01447
01448
01449
01450
01451
01452
01453
01454
01455
01456
01457
01458
01459
01460
01461
01462
01463
01464
01465
01466
01467
01468
01469
01470
01471
01472
01473
01474
01475
01476
01477
01478
01479
01480
01481
01482
01483
01484
01485
01486
01487
01488
01489
01490
01491
01492
01493
01494
01495
01496
01497
01498
01499
01500
01501
01502
01503
01504
01505
01506
01507
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517
01518
01519
01520
01521
01522
01523
01524
01525
01526
01527
01528
01529
01530
01531
01532
01533
01534
01535
01536
01537
01538
01539
01540
01541
01542
01543
01544
01545
01546
01547
01548
01549
01550
01551
01552
01553
01554
01555
01556
01557
01558
01559
01560
01561
01562
01563
01564
01565
01566
01567
01568
01569
01570
01571
01572
01573
01574
01575
01576
01577
01578
01579
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594
01595
01596
01597
01598
01599
01600
01601
01602
01603
01604
01605
01606
01607
01608
01609
01610
01611
01612
01613
01614
01615
01616
01617
01618
01619
01620
01621
01622
01623
01624
01625
01626
01627
01628
01629
01630
01631
01632
01633
01634
01635
01636
01637
01638
01639
01640
01641
01642
01643
01644
01645
01646
01647
01648
01649
01650
01651
01652
01653
01654
01655
01656
01657
01658
01659
01660
01661
01662
01663
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679
01680
01681
01682
01683
01684
01685
01686
01687
01688
01689
01690
01691
01692
01693
01694
01695
01696
01697
01698
01699
01700
01701
01702
01703
01704
01705
01706
01707
01708
01709
01710
01711
01712
01713
01714
01715
01716
01717
01718
01719
01720
01721
01722
01723
01724
01725
01726
01727
01728
01729
01730
01731
01732
01733
01734
01735
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746
01747
01748
01749
01750
01751
01752
01753
01754
01755
01756
01757
01758
01759
01760
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888
01889
01890
01891
01892
01893
01894
01895
01896
01897
01898
01899
01900
01901
01902
01903
01904
01905
01906
01907
01908
01909
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921
01922
01923
01924
01925
01926
01927
01928
01929
01930
01931
01932
01933
01934
01935
01936
01937
01938
01939
01940
01941
01942
01943
01944
01945
01946
01947
01948
01949
01950
01951
01952
01953
01954
01955
01956
01957
01958
01959
01960
01961
01962
01963
01964
01965
01966
01967
01968
01969
01970
01971
01972
01973
01974
01975
01976
01977
01978
01979
01980
01981
01982
01983
01984
01985
01986
01987
01988
01989
01990
01991
01992
01993
01994
01995
01996
01997
01998
01999
02000
02001
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011
02012
02013
02014
02015
02016
02017
02018
02019
02020
02021
02022
02023
02024
02025
02026
02027
02028
02029
02030
02031
02032
02033
02034
02035
02036
02037
02038
02039
02040
02041
02042
02043
02044
02045
02046
02047
02048
02049
02050
02051
02052
02053
02054
02055
02056
02057
02058
02059
02060
02061
02062
02063
02064
02065
02066
02067
02068
02069
02070
02071
02072
02073
02074
02075
02076
02077
02078
02079
02080
02081
02082
02083
02084
02085
02086
02087
02088
02089
02090
02091
02092
02093
02094
02095
02096
02097
02098
02099
02100
02101
02102
02103
02104
02105
02106
02107
02108
02109
02110
02111
02112
02113
02114
02115
02116
02117
02118
02119
02120
02121
02122
02123
02124
02125
02126
02127
02128
02129
02130
02131
02132
02133
02134
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146
02147
02148
02149
02150
02151
02152
02153
02154
02155
02156
02157
02158
02159
02160
02161
02162
02163
02164
02165
02166
02167
02168
02169
02170
02171
02172
02173
02174
02175
02176
02177
02178
02179
02180
02181
02182
02183
02184
02185
02186
02187
02188
02189
02190
02191
02192
02193
02194
02195
02196
02197
02198
02199
02200
02201
02202
02203
02204
02205
02206
02207
02208
02209
02210
02211
02212
02213
02214
02215
02216
02217
02218
02219
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229
02230
02231
02232
02233
02234
02235
02236
02237
02238
02239
02240
02241
02242
02243
02244
02245
02246
02247
02248
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258
02259
02260
02261
02262
02263
02264
02265
02266
02267
02268
02269
02270
02271
02272
02273
02274
02275
02276
02277
02278
02279
02280
02281
02282
02283
02284
02285
02286
02287
02288
02289
02290
02291
02292
02293
02294
02295
02296
02297
02298
02299
02300
02301
02302
02303
02304
02305
02306
02307
02308
02309
02310
02311
02312
02313
02314
02315
02316
02317
02318
02319
02320
02321
02322
02323
02324
02325
02326
02327
02328
02329
02330
02331
02332
02333
02334
02335
02336
02337
02338
02339
02340
02341
02342
02343
02344
02345
02346
02347
02348
02349
02350
02351
02352
02353
02354
02355
02356
02357
02358
02359
02360
02361
02362
02363
02364
02365
02366
02367
02368
02369
02370
02371
02372
02373
02374
02375
02376
02377
02378
02379
02380
02381
02382
02383
02384
02385
02386
02387
02388
02389
02390
02391
02392
02393
02394
02395
02396
02397
02398
02399
02400
02401
02402
02403
02404
02405
02406
02407
02408
02409
02410
02411
02412
02413
02414
02415
02416
02417
02418
02419
02420
02421
02422
02423
02424
02425
02426
02427
02428
02429
02430
02431
02432
02433
02434
02435
02436
02437
02438
02439
02440
02441
02442
02443
02444
02445
02446
02447
02448
02449
02450
02451
02452
02453
02454
02455
02456
02457
02458
02459
02460
02461
02462
02463
02464
02465
02466
02467
02468
02469
02470
02471
02472
02473
02474
02475
02476
02477
02478
02479
02480
02481
02482
02483
02484
02485
02486
02487
02488
02489
02490
02491
02492
02493
02494
02495
02496
02497
02498
02499
02500
02501
02502
02503
02504
02505
02506
02507
02508
02509
02510
02511
02512
02513
02514
02515
02516
02517
02518
02519
02520
02521
02522
02523
02524
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534
02535
02536
02537
02538
02539
02540
02541
02542
02543
02544
02545
02546
02547
02548
02549
02550
02551
02552
02553
02554
02555
02556
02557
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568
02569
02570
02571
02572
02573
02574
02575
02576
02577
02578
02579
02580
02581
02582
02583
02584
02585
02586
02587
02588
02589
02590
02591
02592
02593
02594
02595
02596
02597
02598
02599
02600
02601
02602
02603
02604
02605
02606
02607
02608
02609
02610
02611
02612
02613
02614
02615
02616
02617
02618
02619
02620
02621
02622
02623
02624
02625
02626
02627
02628
02629
02630
02631
02632
02633
02634
02635
02636
02637
02638
02639
02640
02641
02642
02643
02644
02645
02646
02647
02648
02649
02650
02651
02652
02653
02654
02655
02656
02657
02658
02659
02660
02661
02662
02663
02664
02665
02666
02667
02668
02669
02670
02671
02672
02673
02674
02675
02676
02677
02678
02679
02680
02681
02682
02683
02684
02685
02686
02687
02688
02689
02690
02691
02692
02693
02694
02695
02696
02697
02698
02699
02700
02701
02702
02703
02704
02705
02706
02707
02708
02709
02710
02711
02712
02713
02714
02715
02716
02717
02718
02719
02720
02721
02722
02723
02724
02725
02726
02727
02728
02729
02730
02731
02732
02733
02734
02735
02736
02737
02738
02739
02740
02741
02742
02743
02744
02745
02746
02747
02748
02749
02750
02751
02752
02753
02754
02755
02756
02757
02758
02759
02760
02761
02762
02763
02764
02765
02766
02767
02768
02769
02770
02771
02772
02773
02774
02775
02776
02777
02778
02779
02780
02781
02782
02783
02784
02785
02786
02787
02788
02789
02790
02791
02792
02793
02794
02795
02796
02797
02798
02799
02800
02801
02802
02803
02804
02805
02806
02807
02808
02809
02810
02811
02812
02813
02814
02815
02816
02817
02818
02819
02820
02821
02822
02823
02824
02825
02826
02827
02828
02829
02830
02831
02832
02833
02834
02835
02836
02837
02838
02839
02840
02841
02842
02843
02844
02845
02846
02847
02848
02849
02850
02851
02852
02853
02854
02855
02856
02857
02858
02859
02860
02861
02862
02863
02864
02865
02866
02867
02868
02869
02870
02871
02872
02873
02874
02875
02876
02877
02878
02879
02880
02881
02882
02883
02884
02885
02886
02887
02888
02889
02890
02891
02892
02893
02894
02895
02896
02897
02898
02899
02900
02901
02902
02903
02904
02905
02906
02907
02908
02909
02910
02911
02912
02913
02914
02915
02916
02917
02918
02919
02920
02921
02922
02923
02924
02925
02926
02927
02928
02929
02930
02931
02932
02933
02934
02935
02936
02937
02938
02939
02940
02941
02942
02943
02944
02945
02946
02947
02948
02949
02950
02951
02952
02953
02954
02955
02956
02957
02958
02959
02960
02961
02962
02963
02964
02965
02966
02967
02968
02969
02970
02971
02972
02973
02974
02975
02976
02977
02978
02979
02980
02981
02982
02983
02984
02985
02986
02987
02988
02989
02990
02991
02992
02993
02994
02995
02996
02997
02998
02999
03000
03001
03002
03003
03004
03005
03006
03007
03008
03009
03010
03011
03012
03013
03014
03015
03016
03017
03018
03019
03020
03021
03022
03023
03024
03025
03026
03027
03028
03029
03030
03031
03032
03033
03034
03035
03036
03037
03038
03039
03040
03041
03042
03043
03044
03045
03046
03047
03048
03049
03050
03051
03052
03053
03054
03055
03056
03057
03058
03059
03060
03061
03062
03063
03064
03065
03066
03067
03068
03069
03070
03071
03072
03073
03074
03075
03076
03077
03078
03079
03080
03081
03082
03083
03084
03085
03086
03087
03088
03089
03090
03091
03092
03093
03094
03095
03096
03097
03098
03099
03100
03101
03102
03103
03104
03105
03106
03107
03108
03109
03110
03111
03112
03113
03114
03115
03116
03117
03118
03119
03120
03121
03122
03123
03124
03125
03126
03127
03128
03129
03130
03131
03132
03133
03134
03135
03136
03137
03138
03139
03140
03141
03142
03143
03144
03145
03146
03147
03148
03149
03150
03151
03152
03153
03154
03155
03156
03157
03158
03159
03160
03161
03162
03163
03164
03165
03166
03167
03168
03169
03170
03171
03172
03173
03174
03175
03176
03177
03178
03179
03180
03181
03182
03183
03184
03185
03186
03187
03188
03189
03190
03191
03192
03193
03194
03195
03196
03197
03198
03199
03200
03201
03202
03203
03204
03205
03206
03207
03208
03209
03210
03211
03212
03213
03214
03215
03216
03217
03218
03219
03220
03221
03222
03223
03224
03225
03226
03227
03228
03229
03230
03231
03232
03233
03234
03235
03236
03237
03238
03239
03240
03241
03242
03243
03244
03245
03246
03247
03248
03249
03250
03251
03252
03253
03254
03255
03256
03257
03258
03259
03260
03261
03262
03263
03264
03265
03266
03267
03268
03269
03270
03271
03272
03273
03274
03275
03276
03277
03278
03279
03280
03281
03282
03283
03284
03285
03286
03287
03288
03289
03290
03291
03292
03293
03294
03295
03296
03297
03298
03299
03300
03301
03302
03303
03304
03305
03306
03307
03308
03309
03310
03311
03312
03313
03314
03315
03316
03317
03318
03319
03320
03321
03322
03323
03324
03325
03326
03327
03328
03329
03330
03331
03332
03333
03334
03335
03336
03337
03338
03339
03340
03341
03342
03343
03344
03345
03346
03347
03348
03349
03350
03351
03352
03353
03354
03355
03356
03357
03358
03359
03360
03361
03362
03363
03364
03365
03366
03367
03368
03369
03370
03371
03372
03373
03374
03375
03376
03377
03378
03379
03380
03381
03382
03383
03384
03385
03386
03387
03388
03389
03390
03391
03392
03393
03394
03395
03396
03397
03398
03399
03400
03401
03402
03403
03404
03405
03406
03407
03408
03409
03410
03411
03412
03413
03414
03415
03416
03417
03418
03419
03420
03421
03422
03423
03424
03425
03426
03427
03428
03429
03430
03431
03432
03433
03434
03435
03436
03437
03438
03439
03440
03441
03442
03443
03444
03445
03446
03447
03448
03449
03450
03451
03452
03453
03454
03455
03456
03457
03458
03459
03460
03461
03462
03463
03464
03465
03466
03467
03468
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482
03483
03484
03485
03486
03487
03488
03489
03490
03491
03492
03493
03494
03495
03496
03497
03498
03499
03500
03501
03502
03503
03504
03505
03506
03507
03508
03509
03510
03511
03512
03513
03514
03515
03516
03517
03518
03519
03520
03521
03522
03523
03524
03525
03526
03527
03528
03529
03530
03531
03532
03533
03534
03535
03536
03537
03538
03539
03540
03541
03542
03543
03544
03545
03546
03547
03548
03549
03550
03551
03552
03553
03554
03555
03556
03557
03558
03559
03560
03561
03562
03563
03564
03565
03566
03567
03568
03569
03570
03571
03572
03573
03574
03575
03576
03577
03578
03579
03580
03581
03582
03583
03584
03585
03586
03587
03588
03589
03590
03591
03592
03593
03594
03595
03596
03597
03598
03599
03600
03601
03602
03603
03604
03605
03606
03607
03608
03609
03610
03611
03612
03613
03614
03615
03616
03617
03618
03619
03620
03621
03622
03623
03624
03625
03626
03627
03628
03629
03630
03631
03632
03633
03634
03635
03636
03637
03638
03639
03640
03641
03642
03643
03644
03645
03646
03647
03648
03649
03650
03651
03652
03653
03654
03655
03656
03657
03658
03659
03660
03661
03662
03663
03664
03665
03666
03667
03668
03669
03670
03671
03672
03673
03674
03675
03676
03677
03678
03679
03680
03681
03682
03683
03684
03685
03686
03687
03688
03689
03690
03691
03692
03693
03694
03695
03696
03697
03698
03699
03700
03701
03702
03703
03704
03705
03706
03707
03708
03709
03710
03711
03712
03713
03714
03715
03716
03717
03718
03719
03720
03721
03722
03723
03724
03725
03726
03727
03728
03729
03730
03731
03732
03733
03734
03735
03736
03737
03738
03739
03740
03741
03742
03743
03744
03745
03746
03747
03748
03749
03750
03751
03752
03753
03754
03755
03756
03757
03758
03759
03760
03761
03762
03763
03764
03765
03766
03767
03768
03769
03770
03771
03772
03773
03774
03775
03776
03777
03778
03779
03780
03781
03782
03783
03784
03785
03786
03787
03788
03789
03790
03791
03792
03793
03794
03795
03796
03797
03798
03799
03800
03801
03802
03803
03804
03805
03806
03807
03808
03809
03810
03811
03812
03813
03814
03815
03816
03817
03818
03819
03820
03821
03822
03823
03824
03825
03826
03827
03828
03829
03830
03831
03832
03833
03834
03835
03836
03837
03838
03839
03840
03841
03842
03843
03844
03845
03846
03847
03848
03849
03850
03851
03852
03853
03854
03855
03856
03857
03858
03859
03860
03861
03862
03863
03864
03865
03866
03867
03868
03869
03870
03871
03872
03873
03874
03875
03876
03877
03878
03879
03880
03881
03882
03883
03884
03885
03886
03887
03888
03889
03890
03891
03892
03893
03894
03895
03896
03897
03898
03899
03900
03901
03902
03903
03904
03905
03906
03907
03908
03909
03910
03911
03912
03913
03914
03915
03916
03917
03918
03919
03920
03921
03922
03923
03924
03925
03926
03927
03928
03929
03930
03931
03932
03933
03934
03935
03936
03937
03938
03939
03940
03941
03942
03943
03944
03945
03946
03947
03948
03949
03950
03951
03952
03953
03954
03955
03956
03957
03958
03959
03960
03961
03962
03963
03964
03965
03966
03967
03968
03969
03970
03971
03972
03973
03974
03975
03976
03977
03978
03979
03980
03981
03982
03983
03984
03985
03986
03987
03988
03989
03990
03991
03992
03993
03994
03995
03996
03997
03998
03999
04000
04001
04002
04003
04004
04005
04006
04007
04008
04009
04010
04011
04012
04013
04014
04015
04016
04017
04018
04019
04020
04021
04022
04023
04024
04025
04026
04027
04028
04029
04030
04031
04032
04033
04034
04035
04036
04037
04038
04039
04040
04041
04042
04043
04044
04045
04046
04047
04048
04049
04050
04051
04052
04053
04054
04055
04056
04057
04058
04059
04060
04061
04062
04063
04064
04065
04066
04067
04068
04069
04070
04071
04072
04073
04074
04075
04076
04077
04078
04079
04080
04081
04082
04083
04084
04085
04086
04087
04088
04089
04090
04091
04092
04093
04094
04095
04096
04097
04098
04099
04100
04101
04102
04103
04104
04105
04106
04107
04108
04109
04110
04111
04112
04113
04114
04115
04116
04117
04118
04119
04120
04121
04122
04123
04124
04125
04126
04127
04128
04129
04130
04131
04132
04133
04134
04135
04136
04137
04138
04139
04140
04141
04142
04143
04144
04145
04146
04147
04148
04149
04150
04151
04152
04153
04154
04155
04156
04157
04158
04159
04160
04161
04162
04163
04164
04165
04166
04167
04168
04169
04170
04171
04172
04173
04174
04175
04176
04177
04178
04179
04180
04181
04182
04183
04184
04185
04186
04187
04188
04189
04190
04191
04192
04193
04194
04195
04196
04197
04198
04199
04200
04201
04202
04203
04204
04205
04206
04207
04208
04209
04210
04211
04212
04213
04214
04215
04216
04217
04218
04219
04220
04221
04222
04223
04224
04225
04226
04227
04228
04229
04230
04231
04232
04233
04234
04235
04236
04237
04238
04239
04240
04241
04242
04243
04244
04245
04246
04247
04248
04249
04250
04251
04252
04253
04254
04255
04256
04257
04258
04259
04260
04261
04262
04263
04264
04265
04266
04267
04268
04269
04270
04271
04272
04273
04274
04275
04276
04277
04278
04279
04280
04281
04282
04283
04284
04285
04286
04287
04288
04289
04290
04291
04292
04293
04294
04295
04296
04297
04298
04299
04300
04301
04302
04303
04304
04305
04306
04307
04308
04309
04310
04311
04312
04313
04314
04315
04316
04317
04318
04319
04320
04321
04322
04323
04324
04325
04326
04327
04328
04329
04330
04331
04332
04333
04334
04335
04336
04337
04338
04339
04340
04341
04342
04343
04344
04345
04346
04347
04348
04349
04350
04351
04352
04353
04354
04355
04356
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366
04367
04368
04369
04370
04371
04372
04373
04374
04375
04376
04377
04378
04379
04380
04381
04382
04383
04384
04385
04386
04387
04388
04389
04390
04391
04392
04393
04394
04395
04396
04397
04398
04399
04400
04401
04402
04403
04404
04405
04406
04407
04408
04409
04410
04411
04412
04413
04414
04415
04416
04417
04418
04419
04420
04421
04422
04423
04424
04425
04426
04427
04428
04429
04430
04431
04432
04433
04434
04435
04436
04437
04438
04439
04440
04441
04442
04443
04444
04445
04446
04447
04448
04449
04450
04451
04452
04453
04454
04455
04456
04457
04458
04459
04460
04461
04462
04463
04464
04465
04466
04467
04468
04469
04470
04471
04472
04473
04474
04475
04476
04477
04478
04479
04480
04481
04482
04483
04484
04485
04486
04487
04488
04489
04490
04491
04492
04493
04494
04495
04496
04497
04498
04499
04500
04501
04502
04503
04504
04505
04506
04507
04508
04509
04510
04511
04512
04513
04514
04515
04516
04517
04518
04519
04520
04521
04522
04523
04524
04525
04526
04527
04528
04529
04530
04531
04532
04533
04534
04535
04536
04537
04538
04539
04540
04541
04542
04543
04544
04545
04546
04547
04548
04549
04550
04551
04552
04553
04554
04555
04556
04557
04558
04559
04560
04561
04562
04563
04564
04565
04566
04567
04568
04569
04570
04571
04572
04573
04574
04575
04576
04577
04578
04579
04580
04581
04582
04583
04584
04585
04586
04587
04588
04589
04590
04591
04592
04593
04594
04595
04596
04597
04598
04599
04600
04601
04602
04603
04604
04605
04606
04607
04608
04609
04610
04611
04612
04613
04614
04615
04616
04617
04618
04619
04620
04621
04622
04623
04624
04625
04626
04627
04628
04629
04630
04631
04632
04633
04634
04635
04636
04637
04638
04639
04640
04641
04642
04643
04644
04645
04646
04647
04648
04649
04650
04651
04652
04653
04654
04655
04656
04657
04658
04659
04660
04661
04662
04663
04664
04665
04666
04667
04668
04669
04670
04671
04672
04673
04674
04675
04676
04677
04678
04679
04680
04681
04682
04683
04684
04685
04686
04687
04688
04689
04690
04691
04692
04693
04694
04695
04696
04697
04698
04699
04700
04701
04702
04703
04704
04705
04706
04707
04708
04709
04710
04711
04712
04713
04714
04715
04716
04717
04718
04719
04720
04721
04722
04723
04724
04725
04726
04727
04728
04729
04730
04731
04732
04733
04734
04735
04736
04737
04738
04739
04740
04741
04742
04743
04744
04745
04746
04747
04748
04749
04750
04751
04752
04753
04754
04755
04756
04757
04758
04759
04760
04761
04762
04763
04764
04765
04766
04767
04768
04769
04770
04771
04772
04773
04774
04775
04776
04777
04778
04779
04780
04781
04782
04783
04784
04785
04786
04787
04788
04789
04790
04791
04792
04793
04794
04795
04796
04797
04798
04799
04800
04801
04802
04803
04804
04805
04806
04807
04808
04809
04810
04811
04812
04813
04814
04815
04816
04817
04818
04819
04820
04821
04822
04823
04824
04825
04826
04827
04828
04829
04830
04831
04832
04833
04834
04835
04836
04837
04838
04839
04840
04841
04842
04843
04844
04845
04846
04847
04848
04849
04850
04851
04852
04853
04854
04855
04856
04857
04858
04859
04860
04861
04862
04863
04864
04865
04866
04867
04868
04869
04870
04871
04872
04873
04874
04875
04876
04877
04878
04879
04880
04881
04882
04883
04884
04885
04886
04887
04888
04889
04890
04891
04892
04893
04894
04895
04896
04897
04898
04899
04900
04901
04902
04903
04904
04905
04906
04907
04908
04909
04910
04911
04912
04913
04914
04915
04916
04917
04918
04919
04920
04921
04922
04923
04924
04925
04926
04927
04928
04929
04930
04931
04932
04933
04934
04935
04936
04937
04938
04939
04940
04941
04942
04943
04944
04945
04946
04947
04948
04949
04950
04951
04952
04953
04954
04955
04956
04957
04958
04959
04960
04961
04962
04963
04964
04965
04966
04967
04968
04969
04970
04971
04972
04973
04974
04975
04976
04977
04978
04979
04980
04981
04982
04983
04984
04985
04986
04987
04988
04989
04990
04991
04992
04993
04994
04995
04996
04997
04998
04999
05000
05001
05002
05003
05004
05005
05006
05007
05008
05009
05010
05011
05012
05013
05014
05015
05016
05017
05018
05019
05020
05021
05022
05023
05024
05025
05026
05027
05028
05029
05030
05031
05032
05033
05034
05035
05036
05037
05038
05039
05040
05041
05042
05043
05044
05045
05046
05047
05048
05049
05050
05051
05052
05053
05054
05055
05056
05057
05058
05059
05060
05061
05062
05063
05064
05065
05066
05067
05068
05069
05070
05071
05072
05073
05074
05075
05076
05077
05078
05079
05080
05081
05082
05083
05084
05085
05086
05087
05088
05089
05090
05091
05092
05093
05094
05095
05096
05097
05098
05099
05100
05101
05102
05103
05104
05105
05106
05107
05108
05109
05110
05111
05112
05113
05114
05115
05116
05117
05118
05119
05120
05121
05122
05123
05124
05125
05126
05127
05128
05129
05130
05131
05132
05133
05134
05135
05136
05137
05138
05139
05140
05141
05142
05143
05144
05145
05146
05147
05148
05149
05150
05151
05152
05153
05154
05155
05156
05157
05158
05159
05160
05161
05162
05163
05164
05165
05166
05167
05168
05169
05170
05171
05172
05173
05174
05175
05176
05177
05178
05179
05180
05181
05182
05183
05184
05185
05186
05187
05188
05189
05190
05191
05192
05193
05194
05195
05196
05197
05198
05199
05200
05201
05202
05203
05204
05205
05206
05207
05208
05209
05210
05211
05212
05213
05214
05215
05216
05217
05218
05219
05220
05221
05222
05223
05224
05225
05226
05227
05228
05229
05230
05231
05232
05233
05234
05235
05236
05237
05238
05239
05240
05241
05242
05243
05244
05245
05246
05247
05248
05249
05250
05251
05252
05253
05254
05255
05256
05257
05258
05259
05260
05261
05262
05263
05264
05265
05266
05267
05268
05269
05270
05271
05272
05273
05274
05275
05276
05277
05278
05279
05280
05281
05282
05283
05284
05285
05286
05287
05288
05289
05290
05291
05292
05293
05294
05295
05296
05297
05298
05299
05300
05301
05302
05303
05304
05305
05306
05307
05308
05309
05310
05311
05312
05313
05314
05315
05316
05317
05318
05319
05320
05321
05322
05323
05324
05325
05326
05327
05328
05329
05330
05331
05332
05333
05334
05335
05336
05337
05338
05339
05340
05341
05342
05343
05344
05345
05346
05347
05348
05349
05350
05351
05352
05353
05354
05355
05356
05357
05358
05359
05360
05361
05362
05363
05364
05365
05366
05367
05368
05369
05370
05371
05372
05373
05374
05375
05376
05377
05378
05379
05380
05381
05382
05383
05384
05385
05386
05387
05388
05389
05390
05391
05392
05393
05394
05395
05396
05397
05398
05399
05400
05401
05402
05403
05404
05405
05406
05407
05408
05409
05410
05411
05412
05413
05414
05415
05416
05417
05418
05419
05420
05421
05422
05423
05424
05425
05426
05427
05428
05429
05430
05431
05432
05433
05434
05435
05436
05437
05438
05439
05440
05441
05442
05443
05444
05445
05446
05447
05448
05449
05450
05451
05452
05453
05454
05455
05456
05457
05458
05459
05460
05461
05462
05463
05464
05465
05466
05467
05468
05469
05470
05471
05472
05473
05474
05475
05476
05477
05478
05479
05480
05481
05482
05483
05484
05485
05486
05487
05488
05489
05490
05491
05492
05493
05494
05495
05496
05497
05498
05499
05500
05501
05502
05503
05504
05505
05506
05507
05508
05509
05510
05511
05512
05513
05514
05515
05516
05517
05518
05519
05520
05521
05522
05523
05524
05525
05526
05527
05528
05529
05530
05531
05532
05533
05534
05535
05536
05537
05538
05539
05540
05541
05542
05543
05544
05545
05546
05547
05548
05549
05550
05551
05552
05553
05554
05555
05556
05557
05558
05559
05560
05561
05562
05563
05564
05565
05566
05567
05568
05569
05570
05571
05572
05573
05574
05575
05576
05577
05578
05579
05580
05581
05582
05583
05584
05585
05586
05587
05588
05589
05590
05591
05592
05593
05594
05595
05596
05597
05598
05599
05600
05601
05602
05603
05604
05605
05606
05607
05608
05609
05610
05611
05612
05613
05614
05615
05616
05617
05618
05619
05620
05621
05622
05623
05624
05625
05626
05627
05628
05629
05630
05631
05632
05633
05634
05635
05636
05637
05638
05639
05640
05641
05642
05643
05644
05645
05646
05647
05648
05649
05650
05651
05652
05653
05654
05655
05656
05657
05658
05659
05660
05661
05662
05663
05664
05665
05666
05667
05668
05669
05670
05671
05672
05673
05674
05675
05676
05677
05678
05679
05680
05681
05682
05683
05684
05685
05686
05687
05688
05689
05690
05691
05692
05693
05694
05695
05696
05697
05698
05699
05700
05701
05702
05703
05704
05705
05706
05707
05708
05709
05710
05711
05712
05713
05714
05715
05716
05717
05718
05719
05720
05721
05722
05723
05724
05725
05726
05727
05728
05729
05730
05731
05732
05733
05734
05735
05736
05737
05738
05739
05740
05741
05742
05743
05744
05745
05746
05747
05748
05749
05750
05751
05752
05753
05754
05755
05756
05757
05758
05759
05760
05761
05762
05763
05764
05765
05766
05767
05768
05769
05770
05771
05772
05773
05774
05775
05776
05777
05778
05779
05780
05781
05782
05783
05784
05785
05786
05787
05788
05789
05790
05791
05792
05793
05794
05795
05796
05797
05798
05799
05800
05801
05802
05803
05804
05805
05806
05807
05808
05809
05810
05811
05812
05813
05814
05815
05816
05817
05818
05819
05820
05821
05822
05823
05824
05825
05826
05827
05828
05829
05830
05831
05832
05833
05834
05835
05836
05837
05838
05839
05840
05841
05842
05843
05844
05845
05846
05847
05848
05849
05850
05851
05852
05853
05854
05855
05856
05857
05858
05859
05860
05861
05862
05863
05864
05865
05866
05867
05868
05869
05870
05871
05872
05873
05874
05875
05876
05877
05878
05879
05880
05881
05882
05883
05884
05885
05886
05887
05888
05889
05890
05891
05892
05893
05894
05895
05896
05897
05898
05899
05900
05901
05902
05903
05904
05905
05906
05907
05908
05909
05910
05911
05912
05913
05914
05915
05916
05917
05918
05919
05920
05921
05922
05923
05924
05925
05926
05927
05928
05929
05930
05931
05932
05933
05934
05935
05936
05937
05938
05939
05940
05941
05942
05943
05944
05945
05946
05947
05948
05949
05950
05951
05952
05953
05954
05955
05956
05957
05958
05959
05960
05961
05962
05963
05964
05965
05966
05967
05968
05969
05970
05971
05972
05973
05974
05975
05976
05977
05978
05979
05980
05981
05982
05983
05984
05985
05986
05987
05988
05989
05990
05991
05992
05993
05994
05995
05996
05997
05998
05999
06000
06001
06002
06003
06004
06005
06006
06007
06008
06009
06010
06011
06012
06013
06014
06015
06016
06017
06018
06019
06020
06021
06022
06023
06024
06025
06026
06027
06028
06029
06030
06031
06032
06033
06034
06035
06036
06037
06038
06039
06040
06041
06042
06043
06044
06045
06046
06047
06048
06049
06050
06051
06052
06053
06054
06055
06056
06057
06058
06059
06060
06061
06062
06063
06064
06065
06066
06067
06068
06069
06070
06071
06072
06073
06074
06075
06076
06077
06078
06079
06080
06081
06082
06083
06084
06085
06086
06087
06088
06089
06090
06091
06092
06093
06094
06095
06096
06097
06098
06099
06100
06101
06102
06103
06104
06105
06106
06107
06108
06109
06110
06111
06112
06113
06114
06115
06116
06117
06118
06119
06120
06121
06122
06123
06124
06125
06126
06127
06128
06129
06130
06131
06132
06133
06134
06135
06136
06137
06138
06139
06140
06141
06142
06143
06144
06145
06146
06147
06148
06149
06150
06151
06152
06153
06154
06155
06156
06157
06158
06159
06160
06161
06162
06163
06164
06165
06166
06167
06168
06169
06170
06171
06172
06173
06174
06175
06176
06177
06178
06179
06180
06181
06182
06183
06184
06185
06186
06187
06188
06189
06190
06191
06192
06193
06194
06195
06196
06197
06198
06199
06200
06201
06202
06203
06204
06205
06206
06207
06208
06209
06210
06211
06212
06213
06214
06215
06216
06217
06218
06219
06220
06221
06222
06223
06224
06225
06226
06227
06228
06229
06230
06231
06232
06233
06234
06235
06236
06237
06238
06239
06240
06241
06242
06243
06244
06245
06246
06247
06248
06249
06250
06251
06252
06253
06254
06255
06256
06257
06258
06259
06260
06261
06262
06263
06264
06265
06266
06267
06268
06269
06270
06271
06272
06273
06274
06275
06276
06277
06278
06279
06280
06281
06282
06283
06284
06285
06286
06287
06288
06289
06290
06291
06292
06293
06294
06295
06296
06297
06298
06299
06300
06301
06302
06303
06304
06305
06306
06307
06308
06309
06310
06311
06312
06313
06314
06315
06316
06317
06318
06319
06320
06321
06322
06323
06324
06325
06326
06327
06328
06329
06330
06331
06332
06333
06334
06335
06336
06337
06338
06339
06340
06341
06342
06343
06344
06345
06346
06347
06348
06349
06350
06351
06352
06353
06354
06355
06356
06357
06358
06359
06360
06361
06362
06363
06364
06365
06366
06367
06368
06369
06370
06371
06372
06373
06374
06375
06376
06377
06378
06379
06380
06381
06382
06383
06384
06385
06386
06387
06388
06389
06390
06391
06392
06393
06394
06395
06396
06397
06398
06399
06400
06401
06402
06403
06404
06405
06406
06407
06408
06409
06410
06411
06412
06413
06414
06415
06416
06417
06418
06419
06420
06421
06422
06423
06424
06425
06426
06427
06428
06429
06430
06431
06432
06433
06434
06435
06436
06437
06438
06439
06440
06441
06442
06443
06444
06445
06446
06447
06448
06449
06450
06451
06452
06453
06454
06455
06456
06457
06458
06459
06460
06461
06462
06463
06464
06465
06466
06467
06468
06469
06470
06471
06472
06473
06474
06475
06476
06477
06478
06479
06480
06481
06482
06483
06484
06485
06486
06487
06488
06489
06490
06491
06492
06493
06494
06495
06496
06497
06498
06499
06500
06501
06502
06503
06504
06505
06506
06507
06508
06509
06510
06511
06512
06513
06514
06515
06516
06517
06518
06519
06520
06521
06522
06523
06524
06525
06526
06527
06528
06529
06530
06531
06532
06533
06534
06535
06536
06537
06538
06539
06540
06541
06542
06543
06544
06545
06546
06547
06548
06549
06550
06551
06552
06553
06554
06555
06556
06557
06558
06559
06560
06561
06562
06563
06564
06565
06566
06567
06568
06569
06570
06571
06572
06573
06574
06575
06576
06577
06578
06579
06580
06581
06582
06583
06584
06585
06586
06587
06588
06589
06590
06591
06592
06593
06594
06595
06596
06597
06598
06599
06600
06601
06602
06603
06604
06605
06606
06607
06608
06609
06610
06611
06612
06613
06614
06615
06616
06617
06618
06619
06620
06621
06622
06623
06624
06625
06626
06627
06628
06629
06630
06631
06632
06633
06634
06635
06636
06637
06638
06639
06640
06641
06642
06643
06644
06645
06646
06647
06648
06649
06650
06651
06652
06653
06654
06655
06656
06657
06658
06659
06660
06661
06662
06663
06664
06665
06666
06667
06668
06669
06670
06671
06672
06673
06674
06675
06676
06677
06678
06679
06680
06681
06682
06683
06684
06685
06686
06687
06688
06689
06690
06691
06692
06693
06694
06695
06696
06697
06698
06699
06700
06701
06702
06703
06704
06705
06706
06707
06708
06709
06710
06711
06712
06713
06714
06715
06716
06717
06718
06719
06720
06721
06722
06723
06724
06725
06726
06727
06728
06729
06730
06731
06732
06733
06734
06735
06736
06737
06738
06739
06740
06741
06742
06743
06744
06745
06746
06747
06748
06749
06750
06751
06752
06753
06754
06755
06756
06757
06758
06759
06760
06761
06762
06763
06764
06765
06766
06767
06768
06769
06770
06771
06772
06773
06774
06775
06776
06777
06778
06779
06780
06781
06782
06783
06784
06785
06786
06787
06788
06789
06790
06791
06792
06793
06794
06795
06796
06797
06798
06799
06800
06801
06802
06803
06804
06805
06806
06807
06808
06809
06810
06811
06812
06813
06814
06815
06816
06817
06818
06819
06820
06821
06822
06823
06824
06825
06826
06827
06828
06829
06830
06831
06832
06833
06834
06835
06836
06837
06838
06839
06840
06841
06842
06843
06844
06845
06846
06847
06848
06849
06850
06851
06852
06853
06854
06855
06856
06857
06858
06859
06860
06861
06862
06863
06864
06865
06866
06867
06868
06869
06870
06871
06872
06873
06874
06875
06876
06877
06878
06879
06880
06881
06882
06883
06884
06885
06886
06887
06888
06889
06890
06891
06892
06893
06894
06895
06896
06897
06898
06899
06900
06901
06902
06903
06904
06905
06906
06907
06908
06909
06910
06911
06912
06913
06914
06915
06916
06917
06918
06919
06920
06921
06922
06923
06924
06925
06926
06927
06928
06929
06930
06931
06932
06933
06934
06935
06936
06937
06938
06939
06940
06941
06942
06943
06944
06945
06946
06947
06948
06949
06950
06951
06952
06953
06954
06955
06956
06957
06958
06959
06960
06961
06962
06963
06964
06965
06966
06967
06968
06969
06970
06971
06972
06973
06974
06975
06976
06977
06978
06979
06980
06981
06982
06983
06984
06985
06986
06987
06988
06989
06990
06991
06992
06993
06994
06995
06996
06997
06998
06999
07000
07001
07002
07003
07004
07005
07006
07007
07008
07009
07010
07011
07012
07013
07014
07015
07016
07017
07018
07019
07020
07021
07022
07023
07024
07025
07026
07027
07028
07029
07030
07031
07032
07033
07034
07035
07036
07037
07038
07039
07040
07041
07042
07043
07044
07045
07046
07047
07048
07049
07050
07051
07052
07053
07054
07055
07056
07057
07058
07059
07060
07061
07062
07063
07064
07065
07066
07067
07068
07069
07070
07071
07072
07073
07074
07075
07076
07077
07078
07079
07080
07081
07082
07083
07084
07085
07086
07087
07088
07089
07090
07091
07092
07093
07094
07095
07096
07097
07098
07099
07100
07101
07102
07103
07104
07105
07106
07107
07108
07109
07110
07111
07112
07113
07114
07115
07116
07117
07118
07119
07120
07121
07122
07123
07124
07125
07126
07127
07128
07129
07130
07131
07132
07133
07134
07135
07136
07137
07138
07139
07140
07141
07142
07143
07144
07145
07146
07147
07148
07149
07150
07151
07152
07153
07154
07155
07156
07157
07158
07159
07160
07161
07162
07163
07164
07165
07166
07167
07168
07169
07170
07171
07172
07173
07174
07175
07176
07177
07178
07179
07180
07181
07182
07183
07184
07185
07186
07187
07188
07189
07190
07191
07192
07193
07194
07195
07196
07197
07198
07199
07200
07201
07202
07203
07204
07205
07206
07207
07208
07209
07210
07211
07212
07213
07214
07215
07216
07217
07218
07219
07220
07221
07222
07223
07224
07225
07226
07227
07228
07229
07230
07231
07232
07233
07234
07235
07236
07237
07238
07239
07240
07241
07242
07243
07244
07245
07246
07247
07248
07249
07250
07251
07252
07253
07254
07255
07256
07257
07258
07259
07260
07261
07262
07263
07264
07265
07266
07267
07268
07269
07270
07271
07272
07273
07274
07275
07276
07277
07278
07279
07280
07281
07282
07283
07284
07285
07286
07287
07288
07289
07290
07291
07292
07293
07294
07295
07296
07297
07298
07299
07300
07301
07302
07303
07304
07305
07306
07307
07308
07309
07310
07311
07312
07313
07314
07315
07316
07317
07318
07319
07320
07321
07322
07323
07324
07325
07326
07327
07328
07329
07330
07331
07332
07333
07334
07335
07336
07337
07338
07339
07340
07341
07342
07343
07344
07345
07346
07347
07348
07349
07350
07351
07352
07353
07354
07355
07356
07357
07358
07359
07360
07361
07362
07363
07364
07365
07366
07367
07368
07369
07370
07371
07372
07373
07374
07375
07376
07377
07378
07379
07380
07381
07382
07383
07384
07385
07386
07387
07388
07389
07390
07391
07392
07393
07394
07395
07396
07397
07398
07399
07400
07401
07402
07403
07404
07405
07406
07407
07408
07409
07410
07411
07412
07413
07414
07415
07416
07417
07418
07419
07420
07421
07422
07423
07424
07425
07426
07427
07428
07429
07430
07431
07432
07433
07434
07435
07436
07437
07438
07439
07440
07441
07442
07443
07444
07445
07446
07447
07448
07449
07450
07451
07452
07453
07454
07455
07456
07457
07458
07459
07460
07461
07462
07463
07464
07465
07466
07467
07468
07469
07470
07471
07472
07473
07474
07475
07476
07477
07478
07479
07480
07481
07482
07483
07484
07485
07486
07487
07488
07489
07490
07491
07492
07493
07494
07495
07496
07497
07498
07499
07500
07501
07502
07503
07504
07505
07506
07507
07508
07509
07510
07511
07512
07513
07514
07515
07516
07517
07518
07519
07520
07521
07522
07523
07524
07525
07526
07527
07528
07529
07530
07531
07532
07533
07534
07535
07536
07537
07538
07539
07540
07541
07542
07543
07544
07545
07546
07547
07548
07549
07550
07551
07552
07553
07554
07555
07556
07557
07558
07559
07560
07561
07562
07563
07564
07565
07566
07567
07568
07569
07570
07571
07572
07573
07574
07575
07576
07577
07578
07579
07580
07581
07582
07583
07584
07585
07586
07587
07588
07589
07590
07591
07592
07593
07594
07595
07596
07597
07598
07599
07600
07601
07602
07603
07604
07605
07606
07607
07608
07609
07610
07611
07612
07613
07614
07615
07616
07617
07618
07619
07620
07621
07622
07623
07624
07625
07626
07627
07628
07629
07630
07631
07632
07633
07634
07635
07636
07637
07638
07639
07640
07641
07642
07643
07644
07645
07646
07647
07648
07649
07650
07651
07652
07653
07654
07655
07656
07657
07658
07659
07660
07661
07662
07663
07664
07665
07666
07667
07668
07669
07670
07671
07672
07673
07674
07675
07676
07677
07678
07679
07680
07681
07682
07683
07684
07685
07686
07687
07688
07689
07690
07691
07692
07693
07694
07695
07696
07697
07698
07699
07700
07701
07702
07703
07704
07705
07706
07707
07708
07709
07710
07711
07712
07713
07714
07715
07716
07717
07718
07719
07720
07721
07722
07723
07724
07725
07726
07727
07728
07729
07730
07731
07732
07733
07734
07735
07736
07737
07738
07739
07740
07741
07742
07743
07744
07745
07746
07747
07748
07749
07750
07751
07752
07753
07754
07755
07756
07757
07758
07759
07760
07761
07762
07763
07764
07765
07766
07767
07768
07769
07770
07771
07772
07773
07774
07775
07776
07777
07778
07779
07780
07781
07782
07783
07784
07785
07786
07787
07788
07789
07790
07791
07792
07793
07794
07795
07796
07797
07798
07799
07800
07801
07802
07803
07804
07805
07806
07807
07808
07809
07810
07811
07812
07813
07814
07815
07816
07817
07818
07819
07820
07821
07822
07823
07824
07825
07826
07827
07828
07829
07830
07831
07832
07833
07834
07835
07836
07837
07838
07839
07840
07841
07842
07843
07844
07845
07846
07847
07848
07849
07850
07851
07852
07853
07854
07855
07856
07857
07858
07859
07860
07861
07862
07863
07864
07865
07866
07867
07868
07869
07870
07871
07872
07873
07874
07875
07876
07877
07878
07879
07880
07881
07882
07883
07884
07885
07886
07887
07888
07889
07890
07891
07892
07893
07894
07895
07896
07897
07898
07899
07900
07901
07902
07903
07904
07905
07906
07907
07908
07909
07910
07911
07912
07913
07914
07915
07916
07917
07918
07919
07920
07921
07922
07923
07924
07925
07926
07927
07928
07929
07930
07931
07932
07933
07934
07935
07936
07937
07938
07939
07940
07941
07942
07943
07944
07945
07946
07947
07948
07949
07950
07951
07952
07953
07954
07955
07956
07957
07958
07959
07960
07961
07962
07963
07964
07965
07966
07967
07968
07969
07970
07971
07972
07973
07974
07975
07976
07977
07978
07979
07980
07981
07982
07983
07984
07985
07986
07987
07988
07989
07990
07991
07992
07993
07994
07995
07996
07997
07998
07999
08000
08001
08002
08003
08004
08005
08006
08007
08008
08009
08010
08011
08012
08013
08014
08015
08016
08017
08018
08019
08020
08021
08022
08023
08024
08025
08026
08027
08028
08029
08030
08031
08032
08033
08034
08035
08036
08037
08038
08039
08040
08041
08042
08043
08044
08045
08046
08047
08048
08049
08050
08051
08052
08053
08054
08055
08056
08057
08058
08059
08060
08061
08062
08063
08064
08065
08066
08067
08068
08069
08070
08071
08072
08073
08074
08075
08076
08077
08078
08079
08080
08081
08082
08083
08084
08085
08086
08087
08088
08089
08090
08091
08092
08093
08094
08095
08096
08097
08098
08099
08100
08101
08102
08103
08104
08105
08106
08107
08108
08109
08110
08111
08112
08113
08114
08115
08116
08117
08118
08119
08120
08121
08122
08123
08124
08125
08126
08127
08128
08129
08130
08131
08132
08133
08134
08135
08136
08137
08138
08139
08140
08141
08142
08143
08144
08145
08146
08147
08148
08149
08150
08151
08152
08153
08154
08155
08156
08157
08158
08159
08160
08161
08162
08163
08164
08165
08166
08167
08168
08169
08170
08171
08172
08173
08174
08175
08176
08177
08178
08179
08180
08181
08182
08183
08184
08185
08186
08187
08188
08189
08190
08191
08192
08193
08194
08195
08196
08197
08198
08199
08200
08201
08202
08203
08204
08205
08206
08207
08208
08209
08210
08211
08212
08213
08214
08215
08216
08217
08218
08219
08220
08221
08222
08223
08224
08225
08226
08227
08228
08229
08230
08231
08232
08233
08234
08235
08236
08237
08238
08239
08240
08241
08242
08243
08244
08245
08246
08247
08248
08249
08250
08251
08252
08253
08254
08255
08256
08257
08258
08259
08260
08261
08262
08263
08264
08265
08266
08267
08268
08269
08270
08271
08272
08273
08274
08275
08276
08277
08278
08279
08280
08281
08282
08283
08284
08285
08286
08287
08288
08289
08290
08291
08292
08293
08294
08295
08296
08297
08298
08299
08300
08301
08302
08303
08304
08305
08306
08307
08308
08309
08310
08311
08312
08313
08314
08315
08316
08317
08318
08319
08320
08321
08322
08323
08324
08325
08326
08327
08328
08329
08330
08331
08332
08333
08334
08335
08336
08337
08338
08339
08340
08341
08342
08343
08344
08345
08346
08347
08348
08349
08350
08351
08352
08353
08354
08355
08356
08357
08358
08359
08360
08361
08362
08363
08364
08365
08366
08367
08368
08369
08370
08371
08372
08373
08374
08375
08376
08377
08378
08379
08380
08381
08382
08383
08384
08385
08386
08387
08388
08389
08390
08391
08392
08393
08394
08395
08396
08397
08398
08399
08400
08401
08402
08403
08404
08405
08406
08407
08408
08409
08410
08411
08412
08413
08414
08415
08416
08417
08418
08419
08420
08421
08422
08423
08424
08425
08426
08427
08428
08429
08430
08431
08432
08433
08434
08435
08436
08437
08438
08439
08440
08441
08442
08443
08444
08445
08446
08447
08448
08449
08450
08451
08452
08453
08454
08455
08456
08457
08458
08459
08460
08461
08462
08463
08464
08465
08466
08467
08468
08469
08470
08471
08472
08473
08474
08475
08476
08477
08478
08479
08480
08481
08482
08483
08484
08485
08486
08487
08488
08489
08490
08491
08492
08493
08494
08495
08496
08497
08498
08499
08500
08501
08502
08503
08504
08505
08506
08507
08508
08509
08510
08511
08512
08513
08514
08515
08516
08517
08518
08519
08520
08521
08522
08523
08524
08525
08526
08527
08528
08529
08530
08531
08532
08533
08534
08535
08536
08537
08538
08539
08540
08541
08542
08543
08544
08545
08546
08547
08548
08549
08550
08551
08552
08553
08554
08555
08556
08557
08558
08559
08560
08561
08562
08563
08564
08565
08566
08567
08568
08569
08570
08571
08572
08573
08574
08575
08576
08577
08578
08579
08580
08581
08582
08583
08584
08585
08586
08587
08588
08589
08590
08591
08592
08593
08594
08595
08596
08597
08598
08599
08600
08601
08602
08603
08604
08605
08606
08607
08608
08609
08610
08611
08612
08613
08614
08615
08616
08617
08618
08619
08620
08621
08622
08623
08624
08625
08626
08627
08628
08629
08630
08631
08632
08633
08634
08635
08636
08637
08638
08639
08640
08641
08642
08643
08644
08645
08646
08647
08648
08649
08650
08651
08652
08653
08654
08655
08656
08657
08658
08659
08660
08661
08662
08663
08664
08665
08666
08667
08668
08669
08670
08671
08672
08673
08674
08675
08676
08677
08678
08679
08680
08681
08682
08683
08684
08685
08686
08687
08688
08689
08690
08691
08692
08693
08694
08695
08696
08697
08698
08699
08700
08701
08702
08703
08704
08705
08706
08707
08708
08709
08710
08711
08712
08713
08714
08715
08716
08717
08718
08719
08720
08721
08722
08723
08724
08725
08726
08727
08728
08729
08730
08731
08732
08733
08734
08735
08736
08737
08738
08739
08740
08741
08742
08743
08744
08745
08746
08747
08748
08749
08750
08751
08752
08753
08754
08755
08756
08757
08758
08759
08760
08761
08762
08763
08764
08765
08766
08767
08768
08769
08770
08771
08772
08773
08774
08775
08776
08777
08778
08779
08780
08781
08782
08783
08784
08785
08786
08787
08788
08789
08790
08791
08792
08793
08794
08795
08796
08797
08798
08799
08800
08801
08802
08803
08804
08805
08806
08807
08808
08809
08810
08811
08812
08813
08814
08815
08816
08817
08818
08819
08820
08821
08822
08823
08824
08825
08826
08827
08828
08829
08830
08831
08832
08833
08834
08835
08836
08837
08838
08839
08840
08841
08842
08843
08844
08845
08846
08847
08848
08849
08850
08851
08852
08853
08854
08855
08856
08857
08858
08859
08860
08861
08862
08863
08864
08865
08866
08867
08868
08869
08870
08871
08872
08873
08874
08875
08876
08877
08878
08879
08880
08881
08882
08883
08884
08885
08886
08887
08888
08889
08890
08891
08892
08893
08894
08895
08896
08897
08898
08899
08900
08901
08902
08903
08904
08905
08906
08907
08908
08909
08910
08911
08912
08913
08914
08915
08916
08917
08918
08919
08920
08921
08922
08923
08924
08925
08926
08927
08928
08929
08930
08931
08932
08933
08934
08935
08936
08937
08938
08939
08940
08941
08942
08943
08944
08945
08946
08947
08948
08949
08950
08951
08952
08953
08954
08955
08956
08957
08958
08959
08960
08961
08962
08963
08964
08965
08966
08967
08968
08969
08970
08971
08972
08973
08974
08975
08976
08977
08978
08979
08980
08981
08982
08983
08984
08985
08986
08987
08988
08989
08990
08991
08992
08993
08994
08995
08996
08997
08998
08999
09000
09001
09002
09003
09004
09005
09006
09007
09008
09009
09010
09011
09012
09013
09014
09015
09016
09017
09018
09019
09020
09021
09022
09023
09024
09025
09026
09027
09028
09029
09030
09031
09032
09033
09034
09035
09036
09037
09038
09039
09040
09041
09042
09043
09044
09045
09046
09047
09048
09049
09050
09051
09052
09053
09054
09055
09056
09057
09058
09059
09060
09061
09062
09063
09064
09065
09066
09067
09068
09069
09070
09071
09072
09073
09074
09075
09076
09077
09078
09079
09080
09081
09082
09083
09084
09085
09086
09087
09088
09089
09090
09091
09092
09093
09094
09095
09096
09097
09098
09099
09100
09101
09102
09103
09104
09105
09106
09107
09108
09109
09110
09111
09112
09113
09114
09115
09116
09117
09118
09119
09120
09121
09122
09123
09124
09125
09126
09127
09128
09129
09130
09131
09132
09133
09134
09135
09136
09137
09138
09139
09140
09141
09142
09143
09144
09145
09146
09147
09148
09149
09150
09151
09152
09153
09154
09155
09156
09157
09158
09159
09160
09161
09162
09163
09164
09165
09166
09167
09168
09169
09170
09171
09172
09173
09174
09175
09176
09177
09178
09179
09180
09181
09182
09183
09184
09185
09186
09187
09188
09189
09190
09191
09192
09193
09194
09195
09196
09197
09198
09199
09200
09201
09202
09203
09204
09205
09206
09207
09208
09209
09210
09211
09212
09213
09214
09215
09216
09217
09218
09219
09220
09221
09222
09223
09224
09225
09226
09227
09228
09229
09230
09231
09232
09233
09234
09235
09236
09237
09238
09239
09240
09241
09242
09243
09244
09245
09246
09247
09248
09249
09250
09251
09252
09253
09254
09255
09256
09257
09258
09259
09260
09261
09262
09263
09264
09265
09266
09267
09268
09269
09270
09271
09272
09273
09274
09275
09276
09277
09278
09279
09280
09281
09282
09283
09284
09285
09286
09287
09288
09289
09290
09291
09292
09293
09294
09295
09296
09297
09298
09299
09300
09301
09302
09303
09304
09305
09306
09307
09308
09309
09310
09311
09312
09313
09314
09315
09316
09317
09318
09319
09320
09321
09322
09323
09324
09325
09326
09327
09328
09329
09330
09331
09332
09333
09334
09335
09336
09337
09338
09339
09340
09341
09342
09343
09344
09345
09346
09347
09348
09349
09350
09351
09352
09353
09354
09355
09356
09357
09358
09359
09360
09361
09362
09363
09364
09365
09366
09367
09368
09369
09370
09371
09372
09373
09374
09375
09376
09377
09378
09379
09380
09381
09382
09383
09384
09385
09386
09387
09388
09389
09390
09391
09392
09393
09394
09395
09396
09397
09398
09399
09400
09401
09402
09403
09404
09405
09406
09407
09408
09409
09410
09411
09412
09413
09414
09415
09416
09417
09418
09419
09420
09421
09422
09423
09424
09425
09426
09427
09428
09429
09430
09431
09432
09433
09434
09435
09436
09437
09438
09439
09440
09441
09442
09443
09444
09445
09446
09447
09448
09449
09450
09451
09452
09453
09454
09455
09456
09457
09458
09459
09460
09461
09462
09463
09464
09465
09466
09467
09468
09469
09470
09471
09472
09473
09474
09475
09476
09477
09478
09479
09480
09481
09482
09483
09484
09485
09486
09487
09488
09489
09490
09491
09492
09493
09494
09495
09496
09497
09498
09499
09500
09501
09502
09503
09504
09505
09506
09507
09508
09509
09510
09511
09512
09513
09514
09515
09516
09517
09518
09519
09520
09521
09522
09523
09524
09525
09526
09527
09528
09529
09530
09531
09532
09533
09534
09535
09536
09537
09538
09539
09540
09541
09542
09543
09544
09545
09546
09547
09548
09549
09550
09551
09552
09553
09554
09555
09556
09557
09558
09559
09560
09561
09562
09563
09564
09565
09566
09567
09568
09569
09570
09571
09572
09573
09574
09575
09576
09577
09578
09579
09580
09581
09582
09583
09584
09585
09586
09587
09588
09589
09590
09591
09592
09593
09594
09595
09596
09597
09598
09599
09600
09601
09602
09603
09604
09605
09606
09607
09608
09609
09610
09611
09612
09613
09614
09615
09616
09617
09618
09619
09620
09621
09622
09623
09624
09625
09626
09627
09628
09629
09630
09631
09632
09633
09634
09635
09636
09637
09638
09639
09640
09641
09642
09643
09644
09645
09646
09647
09648
09649
09650
09651
09652
09653
09654
09655
09656
09657
09658
09659
09660
09661
09662
09663
09664
09665
09666
09667
09668
09669
09670
09671
09672
09673
09674
09675
09676
09677
09678
09679
09680
09681
09682
09683
09684
09685
09686
09687
09688
09689
09690
09691
09692
09693
09694
09695
09696
09697
09698
09699
09700
09701
09702
09703
09704
09705
09706
09707
09708
09709
09710
09711
09712
09713
09714
09715
09716
09717
09718
09719
09720
09721
09722
09723
09724
09725
09726
09727
09728
09729
09730
09731
09732
09733
09734
09735
09736
09737
09738
09739
09740
09741
09742
09743
09744
09745
09746
09747
09748
09749
09750
09751
09752
09753
09754
09755
09756
09757
09758
09759
09760
09761
09762
09763
09764
09765
09766
09767
09768
09769
09770
09771
09772
09773
09774
09775
09776
09777
09778
09779
09780
09781
09782
09783
09784
09785
09786
09787
09788
09789
09790
09791
09792
09793
09794
09795
09796
09797
09798
09799
09800
09801
09802
09803
09804
09805
09806
09807
09808
09809
09810
09811
09812
09813
09814
09815
09816
09817
09818
09819
09820
09821
09822
09823
09824
09825
09826
09827
09828
09829
09830
09831
09832
09833
09834
09835
09836
09837
09838
09839
09840
09841
09842
09843
09844
09845
09846
09847
09848
09849
09850
09851
09852
09853
09854
09855
09856
09857
09858
09859
09860
09861
09862
09863
09864
09865
09866
09867
09868
09869
09870
09871
09872
09873
09874
09875
09876
09877
09878
09879
09880
09881
09882
09883
09884
09885
09886
09887
09888
09889
09890
09891
09892
09893
09894
09895
09896
09897
09898
09899
09900
09901
09902
09903
09904
09905
09906
09907
09908
09909
09910
09911
09912
09913
09914
09915
09916
09917
09918
09919
09920
09921
09922
09923
09924
09925
09926
09927
09928
09929
09930
09931
09932
09933
09934
09935
09936
09937
09938
09939
09940
09941
09942
09943
09944
09945
09946
09947
09948
09949
09950
09951
09952
09953
09954
09955
09956
09957
09958
09959
09960
09961
09962
09963
09964
09965
09966
09967
09968
09969
09970
09971
09972
09973
09974
09975
09976
09977
09978
09979
09980
09981
09982
09983
09984
09985
09986
09987
09988
09989
09990
09991
09992
09993
09994
09995
09996
09997
09998
09999
10000
10001
10002
10003
10004
10005
10006
10007
10008
10009
10010
10011
10012
10013
10014
10015
10016
10017
10018
10019
10020
10021
10022
10023
10024
10025
10026
10027
10028
10029
10030
10031
10032
10033
10034
10035
10036
10037
10038
10039
10040
10041
10042
10043
10044
10045
10046
10047
10048
10049
10050
10051
10052
10053
10054
10055
10056
10057
10058
10059
10060
10061
10062
10063
10064
10065
10066
10067
10068
10069
10070
10071
10072
10073
10074
10075
10076
10077
10078
10079
10080
10081
10082
10083
10084
10085
10086
10087
10088
10089
10090
10091
10092
10093
10094
10095
10096
10097
10098
10099
10100
10101
10102
10103
10104
10105
10106
10107
10108
10109
10110
10111
10112
10113
10114
10115
10116
10117
10118
10119
10120
10121
10122
10123
10124
10125
10126
10127
10128
10129
10130
10131
10132
10133
10134
10135
10136
10137
10138
10139
10140
10141
10142
10143
10144
10145
10146
10147
10148
10149
10150
10151
10152
10153
10154
10155
10156
10157
10158
10159
10160
10161
10162
10163
10164
10165
10166
10167
10168
10169
10170
10171
10172
10173
10174
10175
10176
10177
10178
10179
10180
10181
10182
10183
10184
10185
10186
10187
10188
10189
10190
10191
10192
10193
10194
10195
10196
10197
10198
10199
10200
10201
10202
10203
10204
10205
10206
10207
10208
10209
10210
10211
10212
10213
10214
10215
10216
10217
10218
10219
10220
10221
10222
10223
10224
10225
10226
10227
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242
10243
10244
10245
10246
10247
10248
10249
10250
10251
10252
10253
10254
10255
10256
10257
10258
10259
10260
10261
10262
10263
10264
10265
10266
10267
10268
10269
10270
10271
10272
10273
10274
10275
10276
10277
10278
10279
10280
10281
10282
10283
10284
10285
10286
10287
10288
10289
10290
10291
10292
10293
10294
10295
10296
10297
10298
10299
10300
10301
10302
10303
10304
10305
10306
10307
10308
10309
10310
10311
10312
10313
10314
10315
10316
10317
10318
10319
10320
10321
10322
10323
10324
10325
10326
10327
10328
10329
10330
10331
10332
10333
10334
10335
10336
10337
10338
10339
10340
10341
10342
10343
10344
10345
10346
10347
10348
10349
10350
10351
10352
10353
10354
10355
10356
10357
10358
10359
10360
10361
10362
10363
10364
10365
10366
10367
10368
10369
10370
10371
10372
10373
10374
10375
10376
10377
10378
10379
10380
10381
10382
10383
10384
10385
10386
10387
10388
10389
10390
10391
10392
10393
10394
10395
10396
10397
10398
10399
10400
10401
10402
10403
10404
10405
10406
10407
10408
10409
10410
10411
10412
10413
10414
10415
10416
10417
10418
10419
10420
10421
10422
10423
10424
10425
10426
10427
10428
10429
10430
10431
10432
10433
10434
10435
10436
10437
10438
10439
10440
10441
10442
10443
10444
10445
10446
10447
10448
10449
10450
10451
10452
10453
10454
10455
10456
10457
10458
10459
10460
10461
10462
10463
10464
10465
10466
10467
10468
10469
10470
10471
10472
10473
10474
10475
10476
10477
10478
10479
10480
10481
10482
10483
10484
10485
10486
10487
10488
10489
10490
10491
10492
10493
10494
10495
10496
10497
10498
10499
10500
10501
10502
10503
10504
10505
10506
10507
10508
10509
10510
10511
10512
10513
10514
10515
10516
10517
10518
10519
10520
10521
10522
10523
10524
10525
10526
10527
10528
10529
10530
10531
10532
10533
10534
10535
10536
10537
10538
10539
10540
10541
10542
10543
10544
10545
10546
10547
10548
10549
10550
10551
10552
10553
10554
10555
10556
10557
10558
10559
10560
10561
10562
10563
10564
10565
10566
10567
10568
10569
10570
10571
10572
10573
10574
10575
10576
10577
10578
10579
10580
10581
10582
10583
10584
10585
10586
10587
10588
10589
10590
10591
10592
10593
10594
10595
10596
10597
10598
10599
10600
10601
10602
10603
10604
10605
10606
10607
10608
10609
10610
10611
10612
10613
10614
10615
10616
10617
10618
10619
10620
10621
10622
10623
10624
10625
10626
10627
10628
10629
10630
10631
10632
10633
10634
10635
10636
10637
10638
10639
10640
10641
10642
10643
10644
10645
10646
10647
10648
10649
10650
10651
10652
10653
10654
10655
10656
10657
10658
10659
10660
10661
10662
10663
10664
10665
10666
10667
10668
10669
10670
10671
10672
10673
10674
10675
10676
10677
10678
10679
10680
10681
10682
10683
10684
10685
10686
10687
10688
10689
10690
10691
10692
10693
10694
10695
10696
10697
10698
10699
10700
10701
10702
10703
10704
10705
10706
10707
10708
10709
10710
10711
10712
10713
10714
10715
10716
10717
10718
10719
10720
10721
10722
10723
10724
10725
10726
10727
10728
10729
10730
10731
10732
10733
10734
10735
10736
10737
10738
10739
10740
10741
10742
10743
10744
10745
10746
10747
10748
10749
10750
10751
10752
10753
10754
10755
10756
10757
10758
10759
10760
10761
10762
10763
10764
10765
10766
10767
10768
10769
10770
10771
10772
10773
10774
10775
10776
10777
10778
10779
10780
10781
10782
10783
10784
10785
10786
10787
10788
10789
10790
10791
10792
10793
10794
10795
10796
10797
10798
10799
10800
10801
10802
10803
10804
10805
10806
10807
10808
10809
10810
10811
10812
10813
10814
10815
10816
10817
10818
10819
10820
10821
10822
10823
10824
10825
10826
10827
10828
10829
10830
10831
10832
10833
10834
10835
10836
10837
10838
10839
10840
10841
10842
10843
10844
10845
10846
10847
10848
10849
10850
10851
10852
10853
10854
10855
10856
10857
10858
10859
10860
10861
10862
10863
10864
10865
10866
10867
10868
10869
10870
10871
10872
10873
10874
10875
10876
10877
10878
10879
10880
10881
10882
10883
10884
10885
10886
10887
10888
10889
10890
10891
10892
10893
10894
10895
10896
10897
10898
10899
10900
10901
10902
10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
10930
10931
10932
10933
10934
10935
10936
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951
10952
10953
10954
10955
10956
10957
10958
10959
10960
10961
10962
10963
10964
10965
10966
10967
10968
10969
10970
10971
10972
10973
10974
10975
10976
10977
10978
10979
10980
10981
10982
10983
10984
10985
10986
10987
10988
10989
10990
10991
10992
10993
10994
10995
10996
10997
10998
10999
11000
11001
11002
11003
11004
11005
11006
11007
11008
11009
11010
11011
11012
11013
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028
11029
11030
11031
11032
11033
11034
11035
11036
11037
11038
11039
11040
11041
11042
11043
11044
11045
11046
11047
11048
11049
11050
11051
11052
11053
11054
11055
11056
11057
11058
11059
11060
11061
11062
11063
11064
11065
11066
11067
11068
11069
11070
11071
11072
11073
11074
11075
11076
11077
11078
11079
11080
11081
11082
11083
11084
11085
11086
11087
11088
11089
11090
11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
11120
11121
11122
11123
11124
11125
11126
11127
11128
11129
11130
11131
11132
11133
11134
11135
11136
11137
11138
11139
11140
11141
11142
11143
11144
11145
11146
11147
11148
11149
11150
11151
11152
11153
11154
11155
11156
11157
11158
11159
11160
11161
11162
11163
11164
11165
11166
11167
11168
11169
11170
11171
11172
11173
11174
11175
11176
11177
11178
11179
11180
11181
11182
11183
11184
11185
11186
11187
11188
11189
11190
11191
11192
11193
11194
11195
11196
11197
11198
11199
11200
11201
11202
11203
11204
11205
11206
11207
11208
11209
11210
11211
11212
11213
11214
11215
11216
11217
11218
11219
11220
11221
11222
11223
11224
11225
11226
11227
11228
11229
11230
11231
11232
11233
11234
11235
11236
11237
11238
11239
11240
11241
11242
11243
11244
11245
11246
11247
11248
11249
11250
11251
11252
11253
11254
11255
11256
11257
11258
11259
11260
11261
11262
11263
11264
11265
11266
11267
11268
11269
11270
11271
11272
11273
11274
11275
11276
11277
11278
11279
11280
11281
11282
11283
11284
11285
11286
11287
11288
11289
11290
11291
11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306
11307
11308
11309
11310
11311
11312
11313
11314
11315
11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330
11331
11332
11333
11334
11335
11336
11337
11338
11339
11340
11341
11342
11343
11344
11345
11346
11347
11348
11349
11350
11351
11352
11353
11354
11355
11356
11357
11358
11359
11360
11361
11362
11363
11364
11365
11366
11367
11368
11369
11370
11371
11372
11373
11374
11375
11376
11377
11378
11379
11380
11381
11382
11383
11384
11385
11386
11387
11388
11389
11390
11391
11392
11393
11394
11395
11396
11397
11398
11399
11400
11401
11402
11403
11404
11405
11406
11407
11408
11409
11410
11411
11412
11413
11414
11415
11416
11417
11418
11419
11420
11421
11422
11423
11424
11425
11426
11427
11428
11429
11430
11431
11432
11433
11434
11435
11436
11437
11438
11439
11440
11441
11442
11443
11444
11445
11446
11447
11448
11449
11450
11451
11452
11453
11454
11455
11456
11457
11458
11459
11460
11461
11462
11463
11464
11465
11466
11467
11468
11469
11470
11471
11472
11473
11474
11475
11476
11477
11478
11479
11480
11481
11482
11483
11484
11485
11486
11487
11488
11489
11490
11491
11492
11493
11494
11495
11496
11497
11498
11499
11500
11501
11502
11503
11504
11505
11506
11507
11508
11509
11510
11511
11512
11513
11514
11515
11516
11517
11518
11519
11520
11521
11522
11523
11524
11525
11526
11527
11528
11529
11530
11531
11532
11533
11534
11535
11536
11537
11538
11539
11540
11541
11542
11543
11544
11545
11546
11547
11548
11549
11550
11551
11552
11553
11554
11555
11556
11557
11558
11559
11560
11561
11562
11563
11564
11565
11566
11567
11568
11569
11570
11571
11572
11573
11574
11575
11576
11577
11578
11579
11580
11581
11582
11583
11584
11585
11586
11587
11588
11589
11590
11591
11592
11593
11594
11595
11596
11597
11598
11599
11600
11601
11602
11603
11604
11605
11606
11607
11608
11609
11610
11611
11612
11613
11614
11615
11616
11617
11618
11619
11620
11621
11622
11623
11624
11625
11626
11627
11628
11629
11630
11631
11632
11633
11634
11635
11636
11637
11638
11639
11640
11641
11642
11643
11644
11645
11646
11647
11648
11649
11650
11651
11652
11653
11654
11655
11656
11657
11658
11659
11660
11661
11662
11663
11664
11665
11666
11667
11668
11669
11670
11671
11672
11673
11674
11675
11676
11677
11678
11679
11680
11681
11682
11683
11684
11685
11686
11687
11688
11689
11690
11691
11692
11693
11694
11695
11696
11697
11698
11699
11700
11701
11702
11703
11704
11705
11706
11707
11708
11709
11710
11711
11712
11713
11714
11715
11716
11717
11718
11719
11720
11721
11722
11723
11724
11725
11726
11727
11728
11729
11730
11731
11732
11733
11734
11735
11736
11737
11738
11739
11740
11741
11742
11743
11744
11745
11746
11747
11748
11749
11750
11751
11752
11753
11754
11755
11756
11757
11758
11759
11760
11761
11762
11763
11764
11765
11766
11767
11768
11769
11770
11771
11772
11773
11774
11775
11776
11777
11778
11779
11780
11781
11782
11783
11784
11785
11786
11787
11788
11789
11790
11791
11792
11793
11794
11795
11796
11797
11798
11799
11800
11801
11802
11803
11804
11805
11806
11807
11808
11809
11810
11811
11812
11813
11814
11815
11816
11817
11818
11819
11820
11821
11822
11823
11824
11825
11826
11827
11828
11829
11830
11831
11832
11833
11834
11835
11836
11837
11838
11839
11840
11841
11842
11843
11844
11845
11846
11847
11848
11849
11850
11851
11852
11853
11854
11855
11856
11857
11858
11859
11860
11861
11862
11863
11864
11865
11866
11867
11868
11869
11870
11871
11872
11873
11874
11875
11876
11877
11878
11879
11880
11881
11882
11883
11884
11885
11886
11887
11888
11889
11890
11891
11892
11893
11894
11895
11896
11897
11898
11899
11900
11901
11902
11903
11904
11905
11906
11907
11908
11909
11910
11911
11912
11913
11914
11915
11916
11917
11918
11919
11920
11921
11922
11923
11924
11925
11926
11927
11928
11929
11930
11931
11932
11933
11934
11935
11936
11937
11938
11939
11940
11941
11942
11943
11944
11945
11946
11947
11948
11949
11950
11951
11952
11953
11954
11955
11956
11957
11958
11959
11960
11961
11962
11963
11964
11965
11966
11967
11968
11969
11970
11971
11972
11973
11974
11975
11976
11977
11978
11979
11980
11981
11982
11983
11984
11985
11986
11987
11988
11989
11990
11991
11992
11993
11994
11995
11996
11997
11998
11999
12000
12001
12002
12003
12004
12005
12006
12007
12008
12009
12010
12011
12012
12013
12014
12015
12016
12017
12018
12019
12020
12021
12022
12023
12024
12025
12026
12027
12028
12029
12030
12031
12032
12033
12034
12035
12036
12037
12038
12039
12040
12041
12042
12043
12044
12045
12046
12047
12048
12049
12050
12051
12052
12053
12054
12055
12056
12057
12058
12059
12060
12061
12062
12063
12064
12065
12066
12067
12068
12069
12070
12071
12072
12073
12074
12075
12076
12077
12078
12079
12080
12081
12082
12083
12084
12085
12086
12087
12088
12089
12090
12091
12092
12093
12094
12095
12096
12097
12098
12099
12100
12101
12102
12103
12104
12105
12106
12107
12108
12109
12110
12111
12112
12113
12114
12115
12116
12117
12118
12119
12120
12121
12122
12123
12124
12125
12126
12127
12128
12129
12130
12131
12132
12133
12134
12135
12136
12137
12138
12139
12140
12141
12142
12143
12144
12145
12146
12147
12148
12149
12150
12151
12152
12153
12154
12155
12156
12157
12158
12159
12160
12161
12162
12163
12164
12165
12166
12167
12168
12169
12170
12171
12172
12173
12174
12175
12176
12177
12178
12179
12180
12181
12182
12183
12184
12185
12186
12187
12188
12189
12190
12191
12192
12193
12194
12195
12196
12197
12198
12199
12200
12201
12202
12203
12204
12205
12206
12207
12208
12209
12210
12211
12212
12213
12214
12215
12216
12217
12218
12219
12220
12221
12222
12223
12224
12225
12226
12227
12228
12229
12230
12231
12232
12233
12234
12235
12236
12237
12238
12239
12240
12241
12242
12243
12244
12245
12246
12247
12248
12249
12250
12251
12252
12253
12254
12255
12256
12257
12258
12259
12260
12261
12262
12263
12264
12265
12266
12267
12268
12269
12270
12271
12272
12273
12274
12275
12276
12277
12278
12279
12280
12281
12282
12283
12284
12285
12286
12287
12288
12289
12290
12291
12292
12293
12294
12295
12296
12297
12298
12299
12300
12301
12302
12303
12304
12305
12306
12307
12308
12309
12310
12311
12312
12313
12314
12315
12316
12317
12318
12319
12320
12321
12322
12323
12324
12325
12326
12327
12328
12329
12330
12331
12332
12333
12334
12335
12336
12337
12338
12339
12340
12341
12342
12343
12344
12345
12346
12347
12348
12349
12350
12351
12352
12353
12354
12355
12356
12357
12358
12359
12360
12361
12362
12363
12364
12365
12366
12367
12368
12369
12370
12371
12372
12373
12374
12375
12376
12377
12378
12379
12380
12381
12382
12383
12384
12385
12386
12387
12388
12389
12390
12391
12392
12393
12394
12395
12396
12397
12398
12399
12400
12401
12402
12403
12404
12405
12406
12407
12408
12409
12410
12411
12412
12413
12414
12415
12416
12417
12418
12419
12420
12421
12422
12423
12424
12425
12426
12427
12428
12429
12430
12431
12432
12433
12434
12435
12436
12437
12438
12439
12440
12441
12442
12443
12444
12445
12446
12447
12448
12449
12450
12451
12452
12453
12454
12455
12456
12457
12458
12459
12460
12461
12462
12463
12464
12465
12466
12467
12468
12469
12470
12471
12472
12473
12474
12475
12476
12477
12478
12479
12480
12481
12482
12483
12484
12485
12486
12487
12488
12489
12490
12491
12492
12493
12494
12495
12496
12497
12498
12499
12500
12501
12502
12503
12504
12505
12506
12507
12508
12509
12510
12511
12512
12513
12514
12515
12516
12517
12518
12519
12520
12521
12522
12523
12524
12525
12526
12527
12528
12529
12530
12531
12532
12533
12534
12535
12536
12537
12538
12539
12540
12541
12542
12543
12544
12545
12546
12547
12548
12549
12550
12551
12552
12553
12554
12555
12556
12557
12558
12559
12560
12561
12562
12563
12564
12565
12566
12567
12568
12569
12570
12571
12572
12573
12574
12575
12576
12577
12578
12579
12580
12581
12582
12583
12584
12585
12586
12587
12588
12589
12590
12591
12592
12593
12594
12595
12596
12597
12598
12599
12600
12601
12602
12603
12604
12605
12606
12607
12608
12609
12610
12611
12612
12613
12614
12615
12616
12617
12618
12619
12620
12621
12622
12623
12624
12625
12626
12627
12628
12629
12630
12631
12632
12633
12634
12635
12636
12637
12638
12639
12640
12641
12642
12643
12644
12645
12646
12647
12648
12649
12650
12651
12652
12653
12654
12655
12656
12657
12658
12659
12660
12661
12662
12663
12664
12665
12666
12667
12668
12669
12670
12671
12672
12673
12674
12675
12676
12677
12678
12679
12680
12681
12682
12683
12684
12685
12686
12687
12688
12689
12690
12691
12692
12693
12694
12695
12696
12697
12698
12699
12700
12701
12702
12703
12704
12705
12706
12707
12708
12709
12710
12711
12712
12713
12714
12715
12716
12717
12718
12719
12720
12721
12722
12723
12724
12725
12726
12727
12728
12729
12730
12731
12732
12733
12734
12735
12736
12737
12738
12739
12740
12741
12742
12743
12744
12745
12746
12747
12748
12749
12750
12751
12752
12753
12754
12755
12756
12757
12758
12759
12760
12761
12762
12763
12764
12765
12766
12767
12768
12769
12770
12771
12772
12773
12774
12775
12776
12777
12778
12779
12780
12781
12782
12783
12784
12785
12786
12787
12788
12789
12790
12791
12792
12793
12794
12795
12796
12797
12798
12799
12800
12801
12802
12803
12804
12805
12806
12807
12808
12809
12810
12811
12812
12813
12814
12815
12816
12817
12818
12819
12820
12821
12822
12823
12824
12825
12826
12827
12828
12829
12830
12831
12832
12833
12834
12835
12836
12837
12838
12839
12840
12841
12842
12843
12844
12845
12846
12847
12848
12849
12850
12851
12852
12853
12854
12855
12856
12857
12858
12859
12860
12861
12862
12863
12864
12865
12866
12867
12868
12869
12870
12871
12872
12873
12874
12875
12876
12877
12878
12879
12880
12881
12882
12883
12884
12885
12886
12887
12888
12889
12890
12891
12892
12893
12894
12895
12896
12897
12898
12899
12900
12901
12902
12903
12904
12905
12906
12907
12908
12909
12910
12911
12912
12913
12914
12915
12916
12917
12918
12919
12920
12921
12922
12923
12924
12925
12926
12927
12928
12929
12930
12931
12932
12933
12934
12935
12936
12937
12938
12939
12940
12941
12942
12943
12944
12945
12946
12947
12948
12949
12950
12951
12952
12953
12954
12955
12956
12957
12958
12959
12960
12961
12962
12963
12964
12965
12966
12967
12968
12969
12970
12971
12972
12973
12974
12975
12976
12977
12978
12979
12980
12981
12982
12983
12984
12985
12986
12987
12988
12989
12990
12991
12992
12993
12994
12995
12996
12997
12998
12999
13000
13001
13002
13003
13004
13005
13006
13007
13008
13009
13010
13011
13012
13013
13014
13015
13016
13017
13018
13019
13020
13021
13022
13023
13024
13025
13026
13027
13028
13029
13030
13031
13032
13033
13034
13035
13036
13037
13038
13039
13040
13041
13042
13043
13044
13045
13046
13047
13048
13049
13050
13051
13052
13053
13054
13055
13056
13057
13058
13059
13060
13061
13062
13063
13064
13065
13066
13067
13068
13069
13070
13071
13072
13073
13074
13075
13076
13077
13078
13079
13080
13081
13082
13083
13084
13085
13086
13087
13088
13089
13090
13091
13092
13093
13094
13095
13096
13097
13098
13099
13100
13101
13102
13103
13104
13105
13106
13107
13108
13109
13110
13111
13112
13113
13114
13115
13116
13117
13118
13119
13120
13121
13122
13123
13124
13125
13126
13127
13128
13129
13130
13131
13132
13133
13134
13135
13136
13137
13138
13139
13140
13141
13142
13143
13144
13145
13146
13147
13148
13149
13150
13151
13152
13153
13154
13155
13156
13157
13158
13159
13160
13161
13162
13163
13164
13165
13166
13167
13168
13169
13170
13171
13172
13173
13174
13175
13176
13177
13178
13179
13180
13181
13182
13183
13184
13185
13186
13187
13188
13189
13190
13191
13192
13193
13194
13195
13196
13197
13198
13199
13200
13201
13202
13203
13204
13205
13206
13207
13208
13209
13210
13211
13212
13213
13214
13215
13216
13217
13218
13219
13220
13221
13222
13223
13224
13225
13226
13227
13228
13229
13230
13231
13232
13233
13234
13235
13236
13237
13238
13239
13240
13241
13242
13243
13244
13245
13246
13247
13248
13249
13250
13251
13252
13253
13254
13255
13256
13257
13258
13259
13260
13261
13262
13263
13264
13265
13266
13267
13268
13269
13270
13271
13272
13273
13274
13275
13276
13277
13278
13279
13280
13281
13282
13283
13284
13285
13286
13287
13288
13289
13290
13291
13292
13293
13294
13295
13296
13297
13298
13299
13300
13301
13302
13303
13304
13305
13306
13307
13308
13309
13310
13311
13312
13313
13314
13315
13316
13317
13318
13319
13320
13321
13322
13323
13324
13325
13326
13327
13328
13329
13330
13331
13332
13333
13334
13335
13336
13337
13338
13339
13340
13341
13342
13343
13344
13345
13346
13347
13348
13349
13350
13351
13352
13353
13354
13355
13356
13357
13358
13359
13360
13361
13362
13363
13364
13365
13366
13367
13368
13369
13370
13371
13372
13373
13374
13375
13376
13377
13378
13379
13380
13381
13382
13383
13384
13385
13386
13387
13388
13389
13390
13391
13392
13393
13394
13395
13396
13397
13398
13399
13400
13401
13402
13403
13404
13405
13406
13407
13408
13409
13410
13411
13412
13413
13414
13415
13416
13417
13418
13419
13420
13421
13422
13423
13424
13425
13426
13427
13428
13429
13430
13431
13432
13433
13434
13435
13436
13437
13438
13439
13440
13441
13442
13443
13444
13445
13446
13447
13448
13449
13450
13451
13452
13453
13454
13455
13456
13457
13458
13459
13460
13461
13462
13463
13464
13465
13466
13467
13468
13469
13470
13471
13472
13473
13474
13475
13476
13477
13478
13479
13480
13481
13482
13483
13484
13485
13486
13487
13488
13489
13490
13491
13492
13493
13494
13495
13496
13497
13498
13499
13500
13501
13502
13503
13504
13505
13506
13507
13508
13509
13510
13511
13512
13513
13514
13515
13516
13517
13518
13519
13520
13521
13522
13523
13524
13525
13526
13527
13528
13529
13530
13531
13532
13533
13534
13535
13536
13537
13538
13539
13540
13541
13542
13543
13544
13545
13546
13547
13548
13549
13550
13551
13552
13553
13554
13555
13556
13557
13558
13559
13560
13561
13562
13563
13564
13565
13566
13567
13568
13569
13570
13571
13572
13573
13574
13575
13576
13577
13578
13579
13580
13581
13582
13583
13584
13585
13586
13587
13588
13589
13590
13591
13592
13593
13594
13595
13596
13597
13598
13599
13600
13601
13602
13603
13604
13605
13606
13607
13608
13609
13610
13611
13612
13613
13614
13615
13616
13617
13618
13619
13620
13621
13622
13623
13624
13625
13626
13627
13628
13629
13630
13631
13632
13633
13634
13635
13636
13637
13638
13639
13640
13641
13642
13643
13644
13645
13646
13647
13648
13649
13650
13651
13652
13653
13654
13655
13656
13657
13658
13659
13660
13661
13662
13663
13664
13665
13666
13667
13668
13669
13670
13671
13672
13673
13674
13675
13676
13677
13678
13679
13680
13681
13682
13683
13684
13685
13686
13687
13688
13689
13690
13691
13692
13693
13694
13695
13696
13697
13698
13699
13700
13701
13702
13703
13704
13705
13706
13707
13708
13709
13710
13711
13712
13713
13714
13715
13716
13717
13718
13719
13720
13721
13722
13723
13724
13725
13726
13727
13728
13729
13730
13731
13732
13733
13734
13735
13736
13737
13738
13739
13740
13741
13742
13743
13744
13745
13746
13747
13748
13749
13750
13751
13752
13753
13754
13755
13756
13757
13758
13759
13760
13761
13762
13763
13764
13765
13766
13767
13768
13769
13770
13771
13772
13773
13774
13775
13776
13777
13778
13779
13780
13781
13782
13783
13784
13785
13786
13787
13788
13789
13790
13791
13792
13793
13794
13795
13796
13797
13798
13799
13800
13801
13802
13803
13804
13805
13806
13807
13808
13809
13810
13811
13812
13813
13814
13815
13816
13817
13818
13819
13820
13821
13822
13823
13824
13825
13826
13827
13828
13829
13830
13831
13832
13833
13834
13835
13836
13837
13838
13839
13840
13841
13842
13843
13844
13845
13846
13847
13848
13849
13850
13851
13852
13853
13854
13855
13856
13857
13858
13859
13860
13861
13862
13863
13864
13865
13866
13867
13868
13869
13870
13871
13872
13873
13874
13875
13876
13877
13878
13879
13880
13881
13882
13883
13884
13885
13886
13887
13888
13889
13890
13891
13892
13893
13894
13895
13896
13897
13898
13899
13900
13901
13902
13903
13904
13905
13906
13907
13908
13909
13910
13911
13912
13913
13914
13915
13916
13917
13918
13919
13920
13921
13922
13923
13924
13925
13926
13927
13928
13929
13930
13931
13932
13933
13934
13935
13936
13937
13938
13939
13940
13941
13942
13943
13944
13945
13946
13947
13948
13949
13950
13951
13952
13953
13954
13955
13956
13957
13958
13959
13960
13961
13962
13963
13964
13965
13966
13967
13968
13969
13970
13971
13972
13973
13974
13975
13976
13977
13978
13979
13980
13981
13982
13983
13984
13985
13986
13987
13988
13989
13990
13991
13992
13993
13994
13995
13996
13997
13998
13999
14000
14001
14002
14003
14004
14005
14006
14007
14008
14009
14010
14011
14012
14013
14014
14015
14016
14017
14018
14019
14020
14021
14022
14023
14024
14025
14026
14027
14028
14029
14030
14031
14032
14033
14034
14035
14036
14037
14038
14039
14040
14041
14042
14043
14044
14045
14046
14047
14048
14049
14050
14051
14052
14053
14054
14055
14056
14057
14058
14059
14060
14061
14062
14063
14064
14065
14066
14067
14068
14069
14070
14071
14072
14073
14074
14075
14076
14077
14078
14079
14080
14081
14082
14083
14084
14085
14086
14087
14088
14089
14090
14091
14092
14093
14094
14095
14096
14097
14098
14099
14100
14101
14102
14103
14104
14105
14106
14107
14108
14109
14110
14111
14112
14113
14114
14115
14116
14117
14118
14119
14120
14121
14122
14123
14124
14125
14126
14127
14128
14129
14130
14131
14132
14133
14134
14135
14136
14137
14138
14139
14140
14141
14142
14143
14144
14145
14146
14147
14148
14149
14150
14151
14152
14153
14154
14155
14156
14157
14158
14159
14160
14161
14162
14163
14164
14165
14166
14167
14168
14169
14170
14171
14172
14173
14174
14175
14176
14177
14178
14179
14180
14181
14182
14183
14184
14185
14186
14187
14188
14189
14190
14191
14192
14193
14194
14195
14196
14197
14198
14199
14200
14201
14202
14203
14204
14205
14206
14207
14208
14209
14210
14211
14212
14213
14214
14215
14216
14217
14218
14219
14220
14221
14222
14223
//=============================================================================
// ScriptedPawn.
//=============================================================================
class ScriptedPawn expands Pawn
    abstract
    native;

// ----------------------------------------------------------------------
// Enumerations

enum EDestinationType  {
    DEST_Failure,
    DEST_NewLocation,
    DEST_SameLocation
};


enum EAllianceType  {
    ALLIANCE_Friendly,
    ALLIANCE_Neutral,
    ALLIANCE_Hostile
};


enum ERaiseAlarmType  {
    RAISEALARM_BeforeAttacking,
    RAISEALARM_BeforeFleeing,
    RAISEALARM_Never
};


enum ESeekType  {
    SEEKTYPE_None,
    SEEKTYPE_Sound,
    SEEKTYPE_Sight,
    SEEKTYPE_Guess,
    SEEKTYPE_Carcass
};


enum EHitLocation  {
    HITLOC_None,
    HITLOC_HeadFront,
    HITLOC_HeadBack,
    HITLOC_TorsoFront,
    HITLOC_TorsoBack,
    HITLOC_LeftLegFront,
    HITLOC_LeftLegBack,
    HITLOC_RightLegFront,
    HITLOC_RightLegBack,
    HITLOC_LeftArmFront,
    HITLOC_LeftArmBack,
    HITLOC_RightArmFront,
    HITLOC_RightArmBack
};


enum ETurning  {
    TURNING_None,
    TURNING_Left,
    TURNING_Right
};


// ----------------------------------------------------------------------
// Structures

struct WanderCandidates  {
    var WanderPoint point;
    var Actor       waypoint;
    var float       dist;
};

struct FleeCandidates  {
    var HidePoint point;
    var Actor     waypoint;
    var Vector    location;
    var float     score;
    var float     dist;
};

struct NearbyProjectile  {
    var DeusExProjectile projectile;
    var vector           location;
    var float            dist;
    var float            range;
};

struct NearbyProjectileList  {
    var NearbyProjectile list[8];
    var vector           center;
};


struct InitialAllianceInfo  {
    var() Name  AllianceName;
    var() float AllianceLevel;
    var() bool  bPermanent;
};

struct AllianceInfoEx  {
    var Name  AllianceName;
    var float AllianceLevel;
    var float AgitationLevel;
    var bool  bPermanent;
};

struct InventoryItem  {
    var() class<Inventory> Inventory;
    var() int              Count;
};


// ----------------------------------------------------------------------
// Variables

var WanderPoint      lastPoints[2];
var float            Restlessness;  // 0-1
var float            Wanderlust;    // 0-1
var float            Cowardice;     // 0-1

var(Combat) float    BaseAccuracy;  // 0-1 or thereabouts
var(Combat) float    MaxRange;
var(Combat) float    MinRange;
var(Combat) float    MinHealth;

var(AI) float    RandomWandering;  // 0-1

var float       sleepTime;
var Actor       destPoint;
var Vector      destLoc;
var Vector      useLoc;
var Rotator     useRot;
var float       seekDistance;  // OBSOLETE
var int         SeekLevel;
var ESeekType   SeekType;
var Pawn        SeekPawn;
var float       CarcassTimer;
var float       CarcassHateTimer;  // OBSOLETE
var float       CarcassCheckTimer;
var name        PotentialEnemyAlliance;
var float       PotentialEnemyTimer;
var float       BeamCheckTimer;
var bool        bSeekPostCombat;
var bool        bSeekLocation;
var bool        bInterruptSeek;
var bool        bAlliancesChanged;         // set to True whenever someone changes AlliancesEx[i].AllianceLevel to indicate we must do alliance updating
var bool        bNoNegativeAlliances;      // True means we know all alliances are currently +, allows us to skip CheckEnemyPresence's slow part

var bool        bSitAnywhere;

var bool        bSitInterpolation;
var bool        bStandInterpolation;
var float       remainingSitTime;
var float       remainingStandTime;
var vector      StandRate;
var float       ReloadTimer;
var bool        bReadyToReload;

var(Pawn) class<carcass> CarcassType;       // mesh to use when killed from the front

// Advanced AI attributes.
var(Orders) name    Orders;         // orders a creature is carrying out 
                                  // will be initial state, plus creature will attempt
                                  // to return to this state
var(Orders) name  OrderTag;       // tag of object referred to by orders
var(Orders) name  HomeTag;        // tag of object to use as home base
var(Orders) float HomeExtent;     // extent of home base
var         actor OrderActor;     // object referred to by orders (if applicable)
var         name  NextAnim;       // used in states with multiple, sequenced animations	
var         float WalkingSpeed;   // 0-1

var(Combat) float ProjectileSpeed;
var         name  LastPainAnim;
var         float LastPainTime;

var         vector DesiredPrePivot;
var         float  PrePivotTime;
var         vector PrePivotOffset;

var     bool        bCanBleed;      // true if an NPC can bleed
var     float       BleedRate;      // how profusely the NPC is bleeding; 0-1
var     float       DropCounter;    // internal; used in tick()
var()   float       ClotPeriod;     // seconds it takes bleedRate to go from 1 to 0

var     bool        bAcceptBump;    // ugly hack
var     bool        bCanFire;       // true if pawn is capable of shooting asynchronously
var(AI) bool        bKeepWeaponDrawn;  // true if pawn should always keep weapon drawn
var(AI) bool        bShowPain;      // true if pawn should play pain animations
var(AI) bool        bCanSit;        // true if pawn can sit
var(AI) bool        bAlwaysPatrol;  // true if stasis should be disabled during patrols
var(AI) bool        bPlayIdle;      // true if pawn should fidget while he's standing
var(AI) bool        bLeaveAfterFleeing;  // true if pawn should disappear after fleeing
var(AI) bool        bLikesNeutral;  // true if pawn should treat neutrals as friendlies
var(AI) bool        bUseFirstSeatOnly;   // true if only the nearest chair should be used for 
var(AI) bool        bCower;         // true if fearful pawns should cower instead of fleeing

var     HomeBase    HomeActor;      // home base
var     Vector      HomeLoc;        // location of home base
var     Vector      HomeRot;        // rotation of home base
var     bool        bUseHome;       // true if home base should be used

var     bool        bInterruptState; // true if the state can be interrupted
var     bool        bCanConverse;    // true if the pawn can converse

var()   bool        bImportant;      // true if this pawn is game-critical
var()   bool        bInvincible;     // true if this pawn cannot be killed

var     bool        bInitialized;    // true if this pawn has been initialized

var(Combat) bool    bAvoidAim;      // avoid enemy's line of fire
var(Combat) bool    bAimForHead;    // aim for the enemy's head
var(Combat) bool    bDefendHome;    // defend the home base
var         bool    bCanCrouch;     // whether we should crouch when firing
var         bool    bSeekCover;     // seek cover
var         bool    bSprint;        // sprint in random directions
var(Combat) bool    bUseFallbackWeapons;  // use fallback weapons even when others are available
var         float   AvoidAccuracy;  // how well we avoid enemy's line of fire; 0-1
var         bool    bAvoidHarm;     // avoid painful projectiles, gas clouds, etc.
var         float   HarmAccuracy;   // how well we avoid harm; 0-1
var         float   CrouchRate;     // how often the NPC crouches, if bCrouch enabled; 0-1
var         float   SprintRate;     // how often the NPC randomly sprints if bSprint enabled; 0-1
var         float   CloseCombatMult;  // multiplier for how often the NPC sprints in close combat; 0-1

// If a stimulation is enabled, it causes an NPC to hate the stimulator
//var(Stimuli) bool   bHateFutz;
var(Stimuli) bool   bHateHacking;  // new
var(Stimuli) bool   bHateWeapon;
var(Stimuli) bool   bHateShot;
var(Stimuli) bool   bHateInjury;
var(Stimuli) bool   bHateIndirectInjury;  // new
//var(Stimuli) bool   bHateGore;
var(Stimuli) bool   bHateCarcass;  // new
var(Stimuli) bool   bHateDistress;
//var(Stimuli) bool   bHateProjectiles;

// If a reaction is enabled, the NPC will react appropriately to a stimulation
var(Reactions) bool bReactFutz;  // new
var(Reactions) bool bReactPresence;         // React to the presence of an enemy (attacking)
var(Reactions) bool bReactLoudNoise;        // Seek the source of a loud noise (seeking)
var(Reactions) bool bReactAlarm;            // Seek the source of an alarm (seeking)
var(Reactions) bool bReactShot;             // React to a gunshot fired by an enemy (attacking)
//var(Reactions) bool bReactGore;             // React to gore appropriately (seeking)
var(Reactions) bool bReactCarcass;          // React to gore appropriately (seeking)
var(Reactions) bool bReactDistress;         // React to distress appropriately (attacking)
var(Reactions) bool bReactProjectiles;      // React to harmful projectiles appropriately

// If a fear is enabled, the NPC will run away from the stimulator
var(Fears) bool     bFearHacking;           // Run away from a hacker
var(Fears) bool     bFearWeapon;            // Run away from a person holding a weapon
var(Fears) bool     bFearShot;              // Run away from a person who fires a shot
var(Fears) bool     bFearInjury;            // Run away from a person who causes injury
var(Fears) bool     bFearIndirectInjury;    // Run away from a person who causes indirect injury
var(Fears) bool     bFearCarcass;           // Run away from a carcass
var(Fears) bool     bFearDistress;          // Run away from a person causing distress
var(Fears) bool     bFearAlarm;             // Run away from the source of an alarm
var(Fears) bool     bFearProjectiles;       // Run away from a projectile

var(AI) bool        bEmitDistress;          // TRUE if NPC should emit distress

var(AI) ERaiseAlarmType RaiseAlarm;         // When to raise an alarm

var     bool        bLookingForEnemy;             // TRUE if we're actually looking for enemies
var     bool        bLookingForLoudNoise;         // TRUE if we're listening for loud noises
var     bool        bLookingForAlarm;             // TRUE if we're listening for alarms
var     bool        bLookingForDistress;          // TRUE if we're looking for signs of distress
var     bool        bLookingForProjectiles;       // TRUE if we're looking for projectiles that can harm us
var     bool        bLookingForFutz;              // TRUE if we're looking for people futzing with stuff
var     bool        bLookingForHacking;           // TRUE if we're looking for people hacking stuff
var     bool        bLookingForShot;              // TRUE if we're listening for gunshots
var     bool        bLookingForWeapon;            // TRUE if we're looking for drawn weapons
var     bool        bLookingForCarcass;           // TRUE if we're looking for carcass events
var     bool        bLookingForInjury;            // TRUE if we're looking for injury events
var     bool        bLookingForIndirectInjury;    // TRUE if we're looking for secondary injury events

var     bool        bFacingTarget;          // True if pawn is facing its target
var(Combat) bool    bMustFaceTarget;        // True if an NPC must face his target to fire
var(Combat) float   FireAngle;              // TOTAL angle (in degrees) in which a pawn may fire if bMustFaceTarget is false
var(Combat) float   FireElevation;          // Max elevation distance required to attack (0=elevation doesn't matter)

var(AI) int         MaxProvocations;
var     float       AgitationSustainTime;
var     float       AgitationDecayRate;
var     float       AgitationTimer;
var     float       AgitationCheckTimer;
var     float       PlayerAgitationTimer;  // hack

var     float       FearSustainTime;
var     float       FearDecayRate;
var     float       FearTimer;
var     float       FearLevel;

var     float       EnemyReadiness;
var     float       ReactionLevel;
var     float       SurprisePeriod;
var     float       SightPercentage;
var     float       CycleTimer;
var     float       CyclePeriod;
var     float       CycleCumulative;
var     Pawn        CycleCandidate;
var     float       CycleDistance;

var     AlarmUnit   AlarmActor;

var     float       AlarmTimer;
var     float       WeaponTimer;
var     float       FireTimer;
var     float       SpecialTimer;
var     float       CrouchTimer;
var     float       BackpedalTimer;

var     bool        bHasShadow;
var     float       ShadowScale;

var     bool        bDisappear;

var     bool        bInTransientState;  // true if the NPC is in a 3rd-tier (transient) state, like TakeHit

var(Alliances) InitialAllianceInfo InitialAlliances[8];
var            AllianceInfoEx      AlliancesEx[16];
var            bool                bReverseAlliances;

var(Pawn) float     BaseAssHeight;

var(AI)   float     EnemyTimeout;
var       float     CheckPeriod;
var       float     EnemyLastSeen;
var       int       SeatSlot;
var       Seat      SeatActor;
var       int       CycleIndex;
var       int       BodyIndex;
var       bool      bRunningStealthy;
var       bool      bPausing;
var       bool      bStaring;
var       bool      bAttacking;
var       bool      bDistressed;
var       bool      bStunned;
var       bool      bSitting;
var       bool      bDancing;
var       bool      bCrouching;

var       bool      bCanTurnHead;

var(AI)   bool      bTickVisibleOnly;   // Temporary?
var()     bool      bInWorld;
var       vector    WorldPosition;
var       bool      bWorldCollideActors;
var       bool      bWorldBlockActors;
var       bool      bWorldBlockPlayers;

var()     bool      bHighlight;         // should this object not highlight when focused?

var(AI)   bool      bHokeyPokey;

var       bool      bConvEndState;

var(Inventory) InventoryItem InitialInventory[8];  // Initial inventory items carried by the pawn

var Bool bConversationEndedNormally;
var Bool bInConversation;
var Actor ConversationActor;                        // Actor currently speaking to or speaking to us

var() sound WalkSound;
var float swimBubbleTimer;
var bool  bSpawnBubbles;

var      bool     bUseSecondaryAttack;

var      bool     bWalkAround;
var      bool     bClearedObstacle;
var      bool     bEnableCheckDest;
var      ETurning TurnDirection;
var      ETurning NextDirection;
var      Actor    ActorAvoiding;
var      float    AvoidWallTimer;
var      float    AvoidBumpTimer;
var      float    ObstacleTimer;
var      vector   LastDestLoc;
var      vector   LastDestPoint;
var      int      DestAttempts;

var      float    DeathTimer;
var      float    EnemyTimer;
var      float    TakeHitTimer;

var      name     ConvOrders;
var      name     ConvOrderTag;

var      float    BurnPeriod;

var      float    FutzTimer;

var      float    DistressTimer;

var      vector   SeatLocation;
var      Seat     SeatHack;
var      bool     bSeatLocationValid;
var      bool     bSeatHackUsed;

var      bool     bBurnedToDeath;

var      bool     bHasCloak;
var      bool     bCloakOn;
var      int      CloakThreshold;
var      float    CloakEMPTimer;

var      float    poisonTimer;      // time remaining before next poison TakeDamage
var      int      poisonCounter;    // number of poison TakeDamages remaining
var      int      poisonDamage;     // damage taken from poison effect
var      Pawn     Poisoner;         // person who initiated PoisonEffect damage

var      Name     Carcasses[4];     // list of carcasses seen
var      int      NumCarcasses;     // number of carcasses seen

var      float    walkAnimMult;
var      float    runAnimMult;

native(2102) final function ConBindEvents();

native(2105) final function bool IsValidEnemy(Pawn TestEnemy, optional bool bCheckAlliance);
native(2106) final function EAllianceType GetAllianceType(Name AllianceName);
native(2107) final function EAllianceType GetPawnAllianceType(Pawn QueryPawn);

native(2108) final function bool HaveSeenCarcass(Name CarcassName);
native(2109) final function AddCarcass(Name CarcassName);

// ----------------------------------------------------------------------
// PreBeginPlay()
// ----------------------------------------------------------------------

function PreBeginPlay()
{
    local float saveBaseEyeHeight;

    // TODO:
    //
    // Okay, we need to save the base eye height right now becase it's
    // obliterated in Pawn.uc with the following:
    //
    //  EyeHeight = 0.8 * CollisionHeight; //FIXME - if all baseeyeheights set right, use them
    //  BaseEyeHeight = EyeHeight;
    //
    // This must be fixed after ECTS.

    saveBaseEyeHeight = BaseEyeHeight;

    Super.PreBeginPlay();

    BaseEyeHeight = saveBaseEyeHeight;

    // create our shadow
    CreateShadow();

    // Set our alliance
    SetAlliance(Alliance);

    // Set up callbacks
    UpdateReactionCallbacks();
}


// ----------------------------------------------------------------------
// PostBeginPlay()
// ----------------------------------------------------------------------

function PostBeginPlay()
{
    Super.PostBeginPlay();

    // Set up pain timer
    if (Region.Zone.bPainZone || HeadRegion.Zone.bPainZone ||
        FootRegion.Zone.bPainZone)
        PainTime = 5.0;
    else if (HeadRegion.Zone.bWaterZone)
        PainTime = UnderWaterTime;

    // Handle holograms
    if ((Style != STY_Masked) && (Style != STY_Normal))
    {
        SetSkinStyle(Style, None);
        SetCollision(false, false, false);
        KillShadow();
        bHasShadow = False;
    }
}


// ----------------------------------------------------------------------
// PostPostBeginPlay()
// ----------------------------------------------------------------------

function PostPostBeginPlay()
{
    Super.PostPostBeginPlay();

    // Bind any conversation events to this ScriptedPawn
    ConBindEvents();
}


// ----------------------------------------------------------------------
// Destroyed()
// ----------------------------------------------------------------------

simulated function Destroyed()
{
    local DeusExPlayer player;

    // Pass a message to conPlay, if it exists in the player, that 
    // this pawn has been destroyed.  This is used to prevent 
    // bad things from happening in converseations.

    player = DeusExPlayer(GetPlayerPawn());

    if ((player != None) && (player.conPlay != None))
        player.conPlay.ActorDestroyed(Self);

    Super.Destroyed();
}

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// GENERAL UTILITIES
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// InitializePawn()
// ----------------------------------------------------------------------

function InitializePawn()
{
    if (!bInitialized)
    {
        InitializeInventory();
        InitializeAlliances();
        InitializeHomeBase();

        BlockReactions();

        if (Alliance != '')
            ChangeAlly(Alliance, 1.0, true);

        if (!bInWorld)
        {
            // tricky
            bInWorld = true;
            LeaveWorld();
        }

        // hack!
        animTimer[1] = 20.0;
        PlayTurnHead(LOOK_Forward, 1.0, 0.0001);

        bInitialized = true;
    }
}


// ----------------------------------------------------------------------
// InitializeInventory()
// ----------------------------------------------------------------------

function InitializeInventory()
{
    local int       i, j;
    local Inventory inv;
    local Weapon    weapons[8];
    local int       weaponCount;
    local Weapon    firstWeapon;

    // Add initial inventory items
    weaponCount = 0;
    for (i=0; i<8; i++)
    {
        if ((InitialInventory[i].Inventory != None) && (InitialInventory[i].Count > 0))
        {
            firstWeapon = None;
            for (j=0; j<InitialInventory[i].Count; j++)
            {
                inv = None;
                if (Class<Ammo>(InitialInventory[i].Inventory) != None)
                {
                    inv = FindInventoryType(InitialInventory[i].Inventory);
                    if (inv != None)
                        Ammo(inv).AmmoAmount += Class<Ammo>(InitialInventory[i].Inventory).default.AmmoAmount;
                }
                if (inv == None)
                {
                    inv = spawn(InitialInventory[i].Inventory, self);
                    if (inv != None)
                    {
                        inv.InitialState='Idle2';
                        inv.GiveTo(Self);
                        inv.SetBase(Self);
                        if ((firstWeapon == None) && (Weapon(inv) != None))
                            firstWeapon = Weapon(inv);
                    }
                }
            }
            if (firstWeapon != None)
                weapons[WeaponCount++] = firstWeapon;
        }
    }
    for (i=0; i<weaponCount; i++)
    {
        if ((weapons[i].AmmoType == None) && (weapons[i].AmmoName != None) &&
            (weapons[i].AmmoName != Class'AmmoNone'))
        {
            weapons[i].AmmoType = Ammo(FindInventoryType(weapons[i].AmmoName));
            if (weapons[i].AmmoType == None)
            {
                weapons[i].AmmoType = spawn(weapons[i].AmmoName);
                weapons[i].AmmoType.InitialState='Idle2';
                weapons[i].AmmoType.GiveTo(Self);
                weapons[i].AmmoType.SetBase(Self);
            }
        }
    }

    SetupWeapon(false);

}


// ----------------------------------------------------------------------
// InitializeAlliances()
// ----------------------------------------------------------------------

function InitializeAlliances()
{
    local int i;

    for (i=0; i<8; i++)
        if (InitialAlliances[i].AllianceName != '')
            ChangeAlly(InitialAlliances[i].AllianceName,
                       InitialAlliances[i].AllianceLevel,
                       InitialAlliances[i].bPermanent);

}


// ----------------------------------------------------------------------
// InitializeHomeBase()
// ----------------------------------------------------------------------

function InitializeHomeBase()
{
    if (!bUseHome)
    {
        HomeActor = None;
        HomeLoc   = Location;
        HomeRot   = vector(Rotation);
        if (HomeTag == 'Start')
            bUseHome = true;
        else
        {
            HomeActor = HomeBase(FindTaggedActor(HomeTag, , Class'HomeBase'));
            if (HomeActor != None)
            {
                HomeLoc    = HomeActor.Location;
                HomeRot    = vector(HomeActor.Rotation);
                HomeExtent = HomeActor.Extent;
                bUseHome   = true;
            }
        }
        HomeRot *= 100;
    }
}


// ----------------------------------------------------------------------
// AddInitialInventory()
// ----------------------------------------------------------------------

function bool AddInitialInventory(class<Inventory> newInventory,
                                  optional int newCount)
{
    local int i;

    if (newCount == 0)
        newCount = 1;

    for (i=0; i<8; i++)
        if ((InitialInventory[i].Inventory == None) &&
            (InitialInventory[i].Count <= 0))
            break;

    if (i < 8)
    {
        InitialInventory[i].Inventory = newInventory;
        InitialInventory[i].Count = newCount;
        return true;
    }
    else
        return false;
}


// ----------------------------------------------------------------------
// SetEnemy()
// ----------------------------------------------------------------------

function bool SetEnemy(Pawn newEnemy, optional float newSeenTime,
                       optional bool bForce)
{
    if (bForce || IsValidEnemy(newEnemy))
    {
        if (newEnemy != Enemy)
            EnemyTimer = 0;
        Enemy         = newEnemy;
        EnemyLastSeen = newSeenTime;

        return True;
    }
    else
        return False;
}


// ----------------------------------------------------------------------
// SetState()
// ----------------------------------------------------------------------

function SetState(Name stateName, optional Name labelName)
{
    if (bInterruptState)
        GotoState(stateName, labelName);
    else
        SetNextState(stateName, labelName);
}


// ----------------------------------------------------------------------
// SetNextState()
// ----------------------------------------------------------------------

function SetNextState(name newState, optional name newLabel)
{
    if (!bInTransientState || !HasNextState())
    {
        if ((newState != 'Conversation') && (newState != 'FirstPersonConversation'))
        {
            NextState = newState;
            NextLabel = newLabel;
        }
    }
}


// ----------------------------------------------------------------------
// ClearNextState()
// ----------------------------------------------------------------------

function ClearNextState()
{
    NextState = '';
    NextLabel = '';
}


// ----------------------------------------------------------------------
// HasNextState()
// ----------------------------------------------------------------------

function bool HasNextState()
{
    if ((NextState == '') || (NextState == GetStateName()))
        return false;
    else
        return true;
}


// ----------------------------------------------------------------------
// GotoNextState()
// ----------------------------------------------------------------------

function GotoNextState()
{
    local bool bSuccess;
    local name oldState, oldLabel;

    if (HasNextState())
    {
        oldState = NextState;
        oldLabel = NextLabel;
        if (oldLabel == '')
            oldLabel = 'Begin';

        ClearNextState();

        GotoState(oldState, oldLabel);
    }
    else
        ClearNextState();
}


// ----------------------------------------------------------------------
// SetOrders()
// ----------------------------------------------------------------------

function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate)
{
    local bool bHostile;
    local Pawn orderEnemy;

    switch (orderName)
    {
        case 'Attacking':
        case 'Fleeing':
        case 'Alerting':
        case 'Seeking':
            bHostile = true;
            break;
        default:
            bHostile = false;
            break;
    }

    if (!bHostile)
    {
        bSeatHackUsed = false;  // hack!
        Orders   = orderName;
        OrderTag = newOrderTag;

        if (bImmediate)
            FollowOrders(true);
    }
    else
    {
        ReactionLevel = 1.0;
        orderEnemy = Pawn(FindTaggedActor(newOrderTag, false, Class'Pawn'));
        if (orderEnemy != None)
        {
            ChangeAlly(orderEnemy.Alliance, -1, true);
            if (SetEnemy(orderEnemy))
                SetState(orderName);
        }
    }

}


// ----------------------------------------------------------------------
// SetHomeBase()
// ----------------------------------------------------------------------

function SetHomeBase(vector baseLocation, optional rotator baseRotator, optional float baseExtent)
{
    local vector vectRot;

    if (baseExtent == 0)
        baseExtent = 800;

    HomeTag    = '';
    HomeActor  = None;
    HomeLoc    = baseLocation;
    HomeRot    = vector(baseRotator)*100;
    HomeExtent = baseExtent;
    bUseHome   = true;
}


// ----------------------------------------------------------------------
// ClearHomeBase()
// ----------------------------------------------------------------------

function ClearHomeBase()
{
    HomeTag  = '';
    bUseHome = false;
}


// ----------------------------------------------------------------------
// IsSeatValid()
// ----------------------------------------------------------------------

function bool IsSeatValid(Actor checkActor)
{
    local PlayerPawn player;
    local Seat       checkSeat;

    checkSeat = Seat(checkActor);
    if (checkSeat == None)
        return false;
    else if (checkSeat.bDeleteMe)
        return false;
    else if (!bSitAnywhere && (VSize(checkSeat.Location-checkSeat.InitialPosition) > 70))
        return false;
    else
    {
        player = GetPlayerPawn();
        if (player != None)
        {
            if (player.CarriedDecoration == checkSeat)
                return false;
        }
        return true;
    }
}


// ----------------------------------------------------------------------
// SetDistress()
// ----------------------------------------------------------------------

function SetDistress(bool bDistress)
{
    bDistressed = bDistress;
    if (bDistress && bEmitDistress)
        AIStartEvent('Distress', EAITYPE_Visual);
    else
        AIEndEvent('Distress', EAITYPE_Visual);
}


// ----------------------------------------------------------------------
// SetDistressTimer()
// ----------------------------------------------------------------------

function SetDistressTimer()
{
    DistressTimer = 0;
}


// ----------------------------------------------------------------------
// SetSeekLocation()
// ----------------------------------------------------------------------

function SetSeekLocation(Pawn seekCandidate, vector newLocation, ESeekType newSeekType, optional bool bNewPostCombat)
{
    SetEnemy(None, 0, true);
    SeekPawn      = seekCandidate;
    LastSeenPos   = newLocation;
    bSeekLocation = True;
    SeekType      = newSeekType;
    if (newSeekType == SEEKTYPE_Carcass)
        CarcassTimer      = 120.0;
    if (newSeekType == SEEKTYPE_Sight)
        SeekLevel = Max(SeekLevel, 1);
    else
        SeekLevel = Max(SeekLevel, 3);
    if (bNewPostCombat)
        bSeekPostCombat = true;
}


// ----------------------------------------------------------------------
// GetCarcassData()
// ----------------------------------------------------------------------

function bool GetCarcassData(actor sender, out Name killer, out Name alliance,
                             out Name CarcassName, optional bool bCheckName)
{
    local DeusExPlayer  dxPlayer;
    local DeusExCarcass carcass;
    local POVCorpse     corpseItem;
    local bool          bCares;
    local bool          bValid;

    alliance = '';
    killer   = '';

    bValid   = false;
    dxPlayer = DeusExPlayer(sender);
    carcass  = DeusExCarcass(sender);
    if (dxPlayer != None)
    {
        corpseItem = POVCorpse(dxPlayer.inHand);
        if (corpseItem != None)
        {
            if (corpseItem.bEmitCarcass)
            {
                alliance    = corpseItem.Alliance;
                killer      = corpseItem.KillerAlliance;
                CarcassName = corpseItem.CarcassName;
                bValid      = true;
            }
        }
    }
    else if (carcass != None)
    {
        if (carcass.bEmitCarcass)
        {
            alliance    = carcass.Alliance;
            killer      = carcass.KillerAlliance;
            CarcassName = carcass.CarcassName;
            bValid      = true;
        }
    }

    bCares = false;
    if (bValid && (!bCheckName || !HaveSeenCarcass(CarcassName)))
    {
        if (bFearCarcass)
            bCares = true;
        else
        {
            if (GetAllianceType(alliance) == ALLIANCE_Friendly)
            {
                if (bHateCarcass)
                    bCares = true;
                else if (bReactCarcass)
                {
                    if (GetAllianceType(killer) == ALLIANCE_Hostile)
                        bCares = true;
                }
            }
        }
    }

    return bCares;
}


// ----------------------------------------------------------------------
// ReactToFutz()
// ----------------------------------------------------------------------

function ReactToFutz()
{
    if (bLookingForFutz && bReactFutz && (FutzTimer <= 0) && !bDistressed)
    {
        FutzTimer = 2.0;
        PlayFutzSound();
    }
}


// ----------------------------------------------------------------------
// ReactToProjectiles()
// ----------------------------------------------------------------------

function ReactToProjectiles(Actor projectileActor)
{
    local DeusExProjectile dxProjectile;
    local Pawn             instigator;

    if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles)
    {
        dxProjectile = DeusExProjectile(projectileActor);
        if ((dxProjectile == None) || IsProjectileDangerous(dxProjectile))
        {
            instigator = Pawn(projectileActor);
            if (instigator == None)
                instigator = projectileActor.Instigator;
            if (instigator != None)
            {
                if (bFearProjectiles)
                    IncreaseFear(instigator, 2.0);
                if (SetEnemy(instigator))
                {
                    SetDistressTimer();
                    HandleEnemy();
                }
                else if (bFearProjectiles && IsFearful())
                {
                    SetDistressTimer();
                    SetEnemy(instigator, , true);
                    GotoState('Fleeing');
                }
                else if (bAvoidHarm)
                    SetState('AvoidingProjectiles');
            }
        }
    }
}


// ----------------------------------------------------------------------
// InstigatorToPawn()
// ----------------------------------------------------------------------

function Pawn InstigatorToPawn(Actor eventActor)
{
    local Pawn pawnActor;

    if (Inventory(eventActor) != None)
    {
        if (Inventory(eventActor).Owner != None)
            eventActor = Inventory(eventActor).Owner;
    }
    else if (DeusExDecoration(eventActor) != None)
        eventActor = GetPlayerPawn();
    else if (DeusExProjectile(eventActor) != None)
        eventActor = eventActor.Instigator;

    pawnActor = Pawn(eventActor);
    if (pawnActor == self)
        pawnActor = None;

    return pawnActor;

}


// ----------------------------------------------------------------------
// EnableShadow()
// ----------------------------------------------------------------------

function EnableShadow(bool bEnable)
{
    if (Shadow != None)
    {
        if (bEnable)
            Shadow.AttachDecal(32,vect(0.1,0.1,0));
        else
            Shadow.DetachDecal();
    }
}


// ----------------------------------------------------------------------
// CreateShadow()
// ----------------------------------------------------------------------

function CreateShadow()
{
    if (bHasShadow && bInWorld)
        if (Shadow == None)
            Shadow = Spawn(class'Shadow', Self,, Location-vect(0,0,1)*CollisionHeight, rot(16384,0,0));
}


// ----------------------------------------------------------------------
// KillShadow()
// ----------------------------------------------------------------------

function KillShadow()
{
    if (Shadow != None)
    {
        Shadow.Destroy();
        Shadow = None;
    }
}


// ----------------------------------------------------------------------
// EnterWorld()
// ----------------------------------------------------------------------

function EnterWorld()
{
    PutInWorld(true);
}


// ----------------------------------------------------------------------
// LeaveWorld()
// ----------------------------------------------------------------------

function LeaveWorld()
{
    PutInWorld(false);
}


// ----------------------------------------------------------------------
// PutInWorld()
// ----------------------------------------------------------------------

function PutInWorld(bool bEnter)
{
    if (bInWorld && !bEnter)
    {
        bInWorld            = false;
        GotoState('Idle');
        bHidden             = true;
        bDetectable         = false;
        WorldPosition       = Location;
        bWorldCollideActors = bCollideActors;
        bWorldBlockActors   = bBlockActors;
        bWorldBlockPlayers  = bBlockPlayers;
        SetCollision(false, false, false);
        bCollideWorld       = false;
        SetPhysics(PHYS_None);
        KillShadow();
        SetLocation(Location+vect(0,0,20000));  // move it out of the way
    }
    else if (!bInWorld && bEnter)
    {
        bInWorld    = true;
        bHidden     = Default.bHidden;
        bDetectable = Default.bDetectable;
        SetLocation(WorldPosition);
        SetCollision(bWorldCollideActors, bWorldBlockActors, bWorldBlockPlayers);
        bCollideWorld = Default.bCollideWorld;
        SetMovementPhysics();
        CreateShadow();
        FollowOrders();
    }
}


// ----------------------------------------------------------------------
// MakePawnIgnored()
// ----------------------------------------------------------------------

function MakePawnIgnored(bool bNewIgnore)
{
    if (bNewIgnore)
    {
        bIgnore = bNewIgnore;
        // to restore original behavior, uncomment the next line
        //bDetectable = !bNewIgnore;
    }
    else
    {
        bIgnore = Default.bIgnore;
        // to restore original behavior, uncomment the next line
        //bDetectable = Default.bDetectable;
    }

}


// ----------------------------------------------------------------------
// EnableCollision() [for sitting state]
// ----------------------------------------------------------------------

function EnableCollision(bool bSet)
{
    EnableShadow(bSet);

    if (bSet)
        SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers);
    else
        SetCollision(True, False, True);
}


// ----------------------------------------------------------------------
// SetBasedPawnSize()
// ----------------------------------------------------------------------

function bool SetBasedPawnSize(float newRadius, float newHeight)
{
    local float  oldRadius, oldHeight;
    local bool   bSuccess;
    local vector centerDelta;
    local float  deltaEyeHeight;

    if (newRadius < 0)
        newRadius = 0;
    if (newHeight < 0)
        newHeight = 0;

    oldRadius = CollisionRadius;
    oldHeight = CollisionHeight;

    if ((oldRadius == newRadius) && (oldHeight == newHeight))
        return true;

    centerDelta    = vect(0, 0, 1)*(newHeight-oldHeight);
    deltaEyeHeight = GetDefaultCollisionHeight() - Default.BaseEyeHeight;

    bSuccess = false;
    if ((newHeight <= CollisionHeight) && (newRadius <= CollisionRadius))  // shrink
    {
        SetCollisionSize(newRadius, newHeight);
        if (Move(centerDelta))
            bSuccess = true;
        else
            SetCollisionSize(oldRadius, oldHeight);
    }
    else
    {
        if (Move(centerDelta))
        {
            SetCollisionSize(newRadius, newHeight);
            bSuccess = true;
        }
    }

    if (bSuccess)
    {
        PrePivotOffset  = vect(0, 0, 1)*(GetDefaultCollisionHeight()-newHeight);
        PrePivot        -= centerDelta;
        DesiredPrePivot -= centerDelta;
        BaseEyeHeight   = newHeight - deltaEyeHeight;
    }

    return (bSuccess);
}


// ----------------------------------------------------------------------
// ResetBasedPawnSize()
// ----------------------------------------------------------------------

function ResetBasedPawnSize()
{
    SetBasedPawnSize(Default.CollisionRadius, GetDefaultCollisionHeight());
}


// ----------------------------------------------------------------------
// GetDefaultCollisionHeight()
// ----------------------------------------------------------------------

function float GetDefaultCollisionHeight()
{
    return (Default.CollisionHeight-4.5);
}


// ----------------------------------------------------------------------
// GetCrouchHeight()
// ----------------------------------------------------------------------

function float GetCrouchHeight()
{
    return (Default.CollisionHeight*0.65);
}


// ----------------------------------------------------------------------
// GetSitHeight()
// ----------------------------------------------------------------------

function float GetSitHeight()
{
    return (GetDefaultCollisionHeight()+(BaseAssHeight*0.5));
}


// ----------------------------------------------------------------------
// IsPointInCylinder()
// ----------------------------------------------------------------------

function bool IsPointInCylinder(Actor cylinder, Vector point,
                                optional float extraRadius, optional float extraHeight)
{
    local bool  bPointInCylinder;
    local float tempX, tempY, tempRad;

    tempX    = cylinder.Location.X - point.X;
    tempX   *= tempX;
    tempY    = cylinder.Location.Y - point.Y;
    tempY   *= tempY;
    tempRad  = cylinder.CollisionRadius + extraRadius;
    tempRad *= tempRad;

    bPointInCylinder = false;
    if (tempX+tempY < tempRad)
        if (Abs(cylinder.Location.Z - point.Z) < (cylinder.CollisionHeight+extraHeight))
            bPointInCylinder = true;

    return (bPointInCylinder);
}


// ----------------------------------------------------------------------
// StartFalling()
// ----------------------------------------------------------------------

function StartFalling(Name resumeState, optional Name resumeLabel)
{
    SetNextState(resumeState, resumeLabel);
    GotoState('FallingState'); 
}


// ----------------------------------------------------------------------
// GetNextWaypoint()
// ----------------------------------------------------------------------

function Actor GetNextWaypoint(Actor destination)
{
    local Actor moveTarget;

    if (destination == None)
        moveTarget = None;
    else if (ActorReachable(destination))
        moveTarget = destination;
    else
        moveTarget = FindPathToward(destination);

    return (moveTarget);
}


// ----------------------------------------------------------------------
// GetNextVector()
// ----------------------------------------------------------------------

function bool GetNextVector(Actor destination, out vector outVect)
{
    local bool    bValid;
    local rotator rot;
    local float   dist;
    local float   maxDist;

    bValid = true;
    if (destination != None)
    {
        maxDist = 64;
        rot     = Rotator(destination.Location - Location);
        dist    = VSize(destination.Location - Location);
        if (dist < maxDist)
            outVect = destination.Location;
        else if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch,
                                       0, maxDist, outVect))
            bValid = false;
    }
    else
        bValid = false;

    return (bValid);
}


// ----------------------------------------------------------------------
// FindOrderActor()
// ----------------------------------------------------------------------

function FindOrderActor()
{
    if (Orders == 'Attacking')
        OrderActor = FindTaggedActor(OrderTag, true, Class'Pawn');
    else
        OrderActor = FindTaggedActor(OrderTag);
}


// ----------------------------------------------------------------------
// FindTaggedActor()
// ----------------------------------------------------------------------

function Actor FindTaggedActor(Name actorTag, optional bool bRandom, optional Class<Actor> tagClass)
{
    local float dist;
    local float bestDist;
    local actor bestActor;
    local actor tempActor;

    bestActor = None;
    bestDist  = 1000000;

    if (tagClass == None)
        tagClass = Class'Actor';

    // if no tag, then assume the player is the target
    if (actorTag == '')
        bestActor = GetPlayerPawn();
    else
    {
        foreach AllActors(tagClass, tempActor, actorTag)
        {
            if (tempActor != self)
            {
                dist = VSize(tempActor.Location - Location);
                if (bRandom)
                    dist *= FRand()*0.6+0.7;  // +/- 30%
                if ((bestActor == None) || (dist < bestDist))
                {
                    bestActor = tempActor;
                    bestDist  = dist;
                }
            }
        }
    }

    return bestActor;
}


// ----------------------------------------------------------------------
// HandleEnemy()
// ----------------------------------------------------------------------

function HandleEnemy()
{
    SetState('HandlingEnemy', 'Begin');
}


// ----------------------------------------------------------------------
// HandleSighting()
// ----------------------------------------------------------------------

function HandleSighting(Pawn pawnSighted)
{
    SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight);
    GotoState('Seeking');
}


// ----------------------------------------------------------------------
// FollowOrders()
// ----------------------------------------------------------------------

function FollowOrders(optional bool bDefer)
{
    local bool bSetEnemy;
    local bool bUseOrderActor;

    if (Orders != '')
    {
        if ((Orders == 'Fleeing') || (Orders == 'Attacking'))
        {
            bSetEnemy      = true;
            bUseOrderActor = true;
        }
        else if ((Orders == 'WaitingFor') || (Orders == 'GoingTo') ||
                 (Orders == 'RunningTo') || (Orders == 'Following') ||
                 (Orders == 'Sitting') || (Orders == 'Shadowing') ||
                 (Orders == 'DebugFollowing') || (Orders == 'DebugPathfinding'))
        {
            bSetEnemy      = false;
            bUseOrderActor = true;
        }
        else
        {
            bSetEnemy      = false;
            bUseOrderActor = false;
        }
        if (bUseOrderActor)
        {
            FindOrderActor();
            if (bSetEnemy)
                SetEnemy(Pawn(OrderActor), 0, true);
        }
        if (bDefer)  // hack
            SetState(Orders);
        else
            GotoState(Orders);
    }
    else
    {
        if (bDefer)
            SetState('Wandering');
        else
            GotoState('Wandering');
    }
}


// ----------------------------------------------------------------------
// ResetConvOrders()
// ----------------------------------------------------------------------

function ResetConvOrders()
{
    ConvOrders   = '';
    ConvOrderTag = '';
}


// ----------------------------------------------------------------------
// GenerateTotalHealth()
//
// this will calculate a weighted average of all of the body parts
// and put that value in the generic Health
// NOTE: head and torso are both critical
// ----------------------------------------------------------------------

function GenerateTotalHealth()
{
    local float limbDamage, headDamage, torsoDamage;

    if (!bInvincible)
    {
        // Scoring works as follows:
        // Disabling the head (100 points damage) will kill you.
        // Disabling the torso (100 points damage) will kill you.
        // Disabling 2 1/2 limbs (250 points damage) will kill you.
        // Combinations can also do you in -- 50 points damage to the head
        // and 125 points damage to the limbs, for example.

        // Note that this formula can produce numbers less than zero, so we'll clamp our
        // health value...

        // Compute total limb damage
        limbDamage  = 0;
        if (Default.HealthLegLeft > 0)
            limbDamage += float(Default.HealthLegLeft-HealthLegLeft)/Default.HealthLegLeft;
        if (Default.HealthLegRight > 0)
            limbDamage += float(Default.HealthLegRight-HealthLegRight)/Default.HealthLegRight;
        if (Default.HealthArmLeft > 0)
            limbDamage += float(Default.HealthArmLeft-HealthArmLeft)/Default.HealthArmLeft;
        if (Default.HealthArmRight > 0)
            limbDamage += float(Default.HealthArmRight-HealthArmRight)/Default.HealthArmRight;
        limbDamage *= 0.4;  // 2 1/2 limbs disabled == death

        // Compute total head damage
        headDamage  = 0;
        if (Default.HealthHead > 0)
            headDamage  = float(Default.HealthHead-HealthHead)/Default.HealthHead;

        // Compute total torso damage
        torsoDamage = 0;
        if (Default.HealthTorso > 0)
            torsoDamage = float(Default.HealthTorso-HealthTorso)/Default.HealthTorso;

        // Compute total health, relative to original health level
        Health = FClamp(Default.Health - ((limbDamage+headDamage+torsoDamage)*Default.Health), 0.0, Default.Health);
    }
    else
    {
        // Pawn is invincible - reset health to defaults
        HealthHead     = Default.HealthHead;
        HealthTorso    = Default.HealthTorso;
        HealthArmLeft  = Default.HealthArmLeft;
        HealthArmRight = Default.HealthArmRight;
        HealthLegLeft  = Default.HealthLegLeft;
        HealthLegRight = Default.HealthLegRight;
        Health         = Default.Health;
    }
}


// ----------------------------------------------------------------------
// UpdatePoison()
// ----------------------------------------------------------------------

function UpdatePoison(float deltaTime)
{
    if ((Health <= 0) || bDeleteMe)  // no more pain -- you're already dead!
        return;

    if (poisonCounter > 0)
    {
        poisonTimer += deltaTime;
        if (poisonTimer >= 2.0)  // pain every two seconds
        {
            poisonTimer = 0;
            poisonCounter--;
            TakeDamage(poisonDamage, Poisoner, Location, vect(0,0,0), 'PoisonEffect');
        }
        if ((poisonCounter <= 0) || (Health <= 0) || bDeleteMe)
            StopPoison();
    }
}


// ----------------------------------------------------------------------
// StartPoison()
// ----------------------------------------------------------------------

function StartPoison(int Damage, Pawn newPoisoner)
{
    if ((Health <= 0) || bDeleteMe)  // no more pain -- you're already dead!
        return;

    poisonCounter = 8;    // take damage no more than eight times (over 16 seconds)
    poisonTimer   = 0;    // reset pain timer
    Poisoner      = newPoisoner;
    if (poisonDamage < Damage)  // set damage amount
        poisonDamage = Damage;
}


// ----------------------------------------------------------------------
// StopPoison()
// ----------------------------------------------------------------------

function StopPoison()
{
    poisonCounter = 0;
    poisonTimer   = 0;
    poisonDamage  = 0;
    Poisoner      = None;
}


// ----------------------------------------------------------------------
// HasEnemyTimedOut()
// ----------------------------------------------------------------------

function bool HasEnemyTimedOut()
{
    if (EnemyTimeout > 0)
    {
        if (EnemyLastSeen > EnemyTimeout)
            return true;
        else
            return false;
    }
    else
        return false;
}


// ----------------------------------------------------------------------
// UpdateActorVisibility()
// ----------------------------------------------------------------------

function UpdateActorVisibility(actor seeActor, float deltaSeconds,
                               float checkTime, bool bCheckDir)
{
    local bool bCanSee;

    CheckPeriod += deltaSeconds;
    if (CheckPeriod >= checkTime)
    {
        CheckPeriod = 0.0;
        if (seeActor != None)
            bCanSee = (AICanSee(seeActor, ComputeActorVisibility(seeActor), false, bCheckDir, true, true) > 0);
        else
            bCanSee = false;
        if (bCanSee)
            EnemyLastSeen = 0;
        else if (EnemyLastSeen <= 0)
            EnemyLastSeen = 0.01;
    }
    if (EnemyLastSeen > 0)
        EnemyLastSeen += deltaSeconds;
}


// ----------------------------------------------------------------------
// ComputeActorVisibility()
// ----------------------------------------------------------------------

function float ComputeActorVisibility(actor seeActor)
{
    local float visibility;

    if (seeActor.IsA('DeusExPlayer'))
        visibility = DeusExPlayer(seeActor).CalculatePlayerVisibility(self);
    else
        visibility = 1.0;

    return (visibility);
}


// ----------------------------------------------------------------------
// UpdateReactionLevel() [internal use only]
// ----------------------------------------------------------------------

function UpdateReactionLevel(bool bRise, float deltaSeconds)
{
    local float surpriseTime;

    // Handle surprise levels...
    if (bRise)
    {
        if (ReactionLevel < 1.0)
        {
            surpriseTime = SurprisePeriod;
            if (surpriseTime <= 0)
                surpriseTime = 0.00000001;
            ReactionLevel += deltaSeconds/surpriseTime;
            if (ReactionLevel > 1.0)
                ReactionLevel = 1.0;
        }
    }
    else
    {
        if (ReactionLevel > 0.0)
        {
            surpriseTime = 7.0;
            ReactionLevel -= deltaSeconds/surpriseTime;
            if (ReactionLevel <= 0.0)
                ReactionLevel = 0.0;
        }
    }
}


// ----------------------------------------------------------------------
// CheckCycle() [internal use only]
// ----------------------------------------------------------------------

function Pawn CheckCycle()
{
    local float attackPeriod;
    local float maxAttackPeriod;
    local float sustainPeriod;
    local float decayPeriod;
    local float minCutoff;
    local Pawn  cycleEnemy;

    attackPeriod    = 0.5;
    maxAttackPeriod = 4.5;
    sustainPeriod   = 3.0;
    decayPeriod     = 4.0;

    minCutoff = attackPeriod/maxAttackPeriod;

    cycleEnemy = None;

    if (CycleCumulative <= 0)  // no enemies seen during this cycle
    {
        CycleTimer -= CyclePeriod;
        if (CycleTimer <= 0)
        {
            CycleTimer = 0;
            EnemyReadiness -= CyclePeriod/decayPeriod;
            if (EnemyReadiness < 0)
                EnemyReadiness = 0;
        }
    }
    else  // I saw somebody!
    {
        CycleTimer = sustainPeriod;
        CycleCumulative *= 2;  // hack
        if (CycleCumulative < minCutoff)
            CycleCumulative = minCutoff;
        else if (CycleCumulative > 1.0)
            CycleCumulative = 1.0;
        EnemyReadiness += CycleCumulative*CyclePeriod/attackPeriod;
        if (EnemyReadiness >= 1.0)
        {
            EnemyReadiness = 1.0;
            if (IsValidEnemy(CycleCandidate))
                cycleEnemy = CycleCandidate;
        }
        else if (EnemyReadiness >= SightPercentage)
            if (IsValidEnemy(CycleCandidate))
                HandleSighting(CycleCandidate);
    }
    CycleCumulative = 0;
    CyclePeriod     = 0;
    CycleCandidate  = None;
    CycleDistance   = 0;

    return (cycleEnemy);

}


// ----------------------------------------------------------------------
// CheckEnemyPresence()
// ----------------------------------------------------------------------

function bool CheckEnemyPresence(float deltaSeconds,
                                 bool bCheckPlayer, bool bCheckOther)
{
    local int          i;
    local int          count;
    local int          checked;
    local Pawn         candidate;
    local float        candidateDist;
    local DeusExPlayer playerCandidate;
    local bool         bCanSee;
    local int          lastCycle;
    local float        visibility;
    local Pawn         cycleEnemy;
    local bool         bValid;
    local bool         bPlayer;
    local float        surpriseTime;
    local bool         bValidEnemy;
    local bool         bPotentialEnemy;
    local bool         bCheck;

    bValid  = false;
    bCanSee = false;
    if (bReactPresence && bLookingForEnemy && !bNoNegativeAlliances)
    {
        if (PotentialEnemyAlliance != '')
            bCheck = true;
        else
        {
            for (i=0; i<16; i++)
                if ((AlliancesEx[i].AllianceLevel < 0) || (AlliancesEx[i].AgitationLevel >= 1.0))
                    break;
            if (i < 16)
                bCheck = true;
        }

        if (bCheck)
        {
            bValid       = true;
            CyclePeriod += deltaSeconds;
            count        = 0;
            checked      = 0;
            lastCycle    = CycleIndex;
            foreach CycleActors(Class'Pawn', candidate, CycleIndex)
            {
                bValidEnemy = IsValidEnemy(candidate);
                if (!bValidEnemy && (PotentialEnemyTimer > 0))
                    if (PotentialEnemyAlliance == candidate.Alliance)
                        bPotentialEnemy = true;
                if (bValidEnemy || bPotentialEnemy)
                {
                    count++;
                    bPlayer = candidate.IsA('PlayerPawn');
                    if ((bPlayer && bCheckPlayer) || (!bPlayer && bCheckOther))
                    {
                        visibility = AICanSee(candidate, ComputeActorVisibility(candidate), true, true, true, true);
                        if (visibility > 0)
                        {
                            if (bPotentialEnemy)  // We can see the potential enemy; ergo, we hate him
                            {
                                IncreaseAgitation(candidate, 1.0);
                                PotentialEnemyAlliance = '';
                                PotentialEnemyTimer    = 0;
                                bValidEnemy = IsValidEnemy(candidate);
                            }
                            if (bValidEnemy)
                            {
                                visibility += VisibilityThreshold;
                                candidateDist = VSize(Location-candidate.Location);
                                if ((CycleCandidate == None) || (CycleDistance > candidateDist))
                                {
                                    CycleCandidate = candidate;
                                    CycleDistance  = candidateDist;
                                }
                                if (!bPlayer)
                                    CycleCumulative += 100000;  // a bit of a hack...
                                else
                                    CycleCumulative += visibility;
                            }
                        }
                    }
                    if (count >= 1)
                        break;
                }
                checked++;
                if (checked > 20)  // hacky hardcoded number
                    break;
            }
            if (lastCycle >= CycleIndex)  // have we cycled through all actors?
            {
                cycleEnemy = CheckCycle();
                if (cycleEnemy != None)
                {
                    SetDistressTimer();
                    SetEnemy(cycleEnemy, 0, true);
                    bCanSee = true;
                }
            }
        }
        else
            bNoNegativeAlliances = True;
    }

    // Handle surprise levels...
    UpdateReactionLevel((EnemyReadiness>0)||(GetStateName()=='Seeking')||bDistressed, deltaSeconds);

    if (!bValid)
    {
        CycleCumulative = 0;
        CyclePeriod     = 0;
        CycleCandidate  = None;
        CycleDistance   = 0;
        CycleTimer      = 0;
    }

    return (bCanSee);

}


// ----------------------------------------------------------------------
// CheckBeamPresence
// ----------------------------------------------------------------------

function bool CheckBeamPresence(float deltaSeconds)
{
    local DeusExPlayer player;
    local Beam         beamActor;
    local bool         bReactToBeam;

    if (bReactPresence && bLookingForEnemy && (BeamCheckTimer <= 0) && (LastRendered() < 5.0))
    {
        BeamCheckTimer = 1.0;
        player = DeusExPlayer(GetPlayerPawn());
        if (player != None)
        {
            bReactToBeam = false;
            if (IsValidEnemy(player))
            {
                foreach RadiusActors(Class'Beam', beamActor, 1200)
                {
                    if ((beamActor.Owner == player) && (beamActor.LightType != LT_None) && (beamActor.LightBrightness > 32))
                    {
                        if (VSize(beamActor.Location - Location) < (beamActor.LightRadius+1)*25)
                            bReactToBeam = true;
                        else
                        {
                            if (AICanSee(beamActor, , false, true, false, false) > 0)
                            {
                                if (FastTrace(beamActor.Location, Location+vect(0,0,1)*BaseEyeHeight))
                                    bReactToBeam = true;
                            }
                        }
                    }
                    if (bReactToBeam)
                        break;
                }
            }
            if (bReactToBeam)
                HandleSighting(player);
        }
    }
}


// ----------------------------------------------------------------------
// CheckCarcassPresence()
// ----------------------------------------------------------------------

function bool CheckCarcassPresence(float deltaSeconds)
{
    local Actor         carcass;
    local Name          CarcassName;
    local int           lastCycle;
    local DeusExCarcass body;
    local DeusExPlayer  player;
    local float         visibility;
    local Name          KillerAlliance;
    local Name          killedAlliance;
    local Pawn          killer;
    local Pawn          bestKiller;
    local float         dist;
    local float         bestDist;
    local float         maxCarcassDist;
    local int           maxCarcassCount;

    if (bFearCarcass && !bHateCarcass && !bReactCarcass)  // Major hack!
        maxCarcassCount = 1;
    else
        maxCarcassCount = ArrayCount(Carcasses);

    //if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass && (CarcassTimer <= 0))
    if ((bHateCarcass || bReactCarcass || bFearCarcass) && (NumCarcasses < maxCarcassCount))
    {
        maxCarcassDist = 1200;
        if (CarcassCheckTimer <= 0)
        {
            CarcassCheckTimer = 0.1;
            carcass           = None;
            lastCycle         = BodyIndex;
            foreach CycleActors(Class'DeusExCarcass', body, BodyIndex)
            {
                if (body.Physics != PHYS_Falling)
                {
                    if (VSize(body.Location-Location) < maxCarcassDist)
                    {
                        if (GetCarcassData(body, KillerAlliance, killedAlliance, CarcassName, true))
                        {
                            visibility = AICanSee(body, ComputeActorVisibility(body), true, true, true, true);
                            if (visibility > 0)
                                carcass = body;
                            break;
                        }
                    }
                }
            }
            if (lastCycle >= BodyIndex)
            {
                if (carcass == None)
                {
                    player = DeusExPlayer(GetPlayerPawn());
                    if (player != None)
                    {
                        if (VSize(player.Location-Location) < maxCarcassDist)
                        {
                            if (GetCarcassData(player, KillerAlliance, killedAlliance, CarcassName, true))
                            {
                                visibility = AICanSee(player, ComputeActorVisibility(player), true, true, true, true);
                                if (visibility > 0)
                                    carcass = player;
                            }
                        }
                    }
                }
            }
            if (carcass != None)
            {
                CarcassTimer = 120;
                AddCarcass(CarcassName);
                if (bLookingForCarcass)
                {
                    if (KillerAlliance == 'Player')
                        killer = GetPlayerPawn();
                    else
                    {
                        bestKiller = None;
                        bestDist   = 0;
                        foreach AllActors(Class'Pawn', killer)  // hack
                        {
                            if (killer.Alliance == KillerAlliance)
                            {
                                dist = VSize(killer.Location - Location);
                                if ((bestKiller == None) || (bestDist > dist))
                                {
                                    bestKiller = killer;
                                    bestDist   = dist;
                                }
                            }
                        }
                        killer = bestKiller;
                    }
                    if (bHateCarcass)
                    {
                        PotentialEnemyAlliance = KillerAlliance;
                        PotentialEnemyTimer    = 15.0;
                        bNoNegativeAlliances   = false;
                    }
                    if (bFearCarcass)
                        IncreaseFear(killer, 2.0);

                    if (bFearCarcass && IsFearful() && !IsValidEnemy(killer))
                    {
                        SetDistressTimer();
                        SetEnemy(killer, , true);
                        GotoState('Fleeing');
                    }
                    else
                    {
                        SetDistressTimer();
                        SetSeekLocation(killer, carcass.Location, SEEKTYPE_Carcass);
                        HandleEnemy();
                    }
                }
            }
        }
    }

}


// ----------------------------------------------------------------------
// AddProjectileToList()
// ----------------------------------------------------------------------

function AddProjectileToList(out NearbyProjectileList projList,
                             DeusExProjectile proj, vector projPos,
                             float dist, float range)
{
    local int   count;
    local int   pos;
    local int   bestPos;
    local float worstDist;

    bestPos   = -1;
    worstDist = dist;
    pos       = 0;
    while (pos < ArrayCount(projList.list))
    {
        if (projList.list[pos].projectile == None)
        {
            bestPos = pos;
            break;  // short-circuit loop
        }
        else
        {
            if (worstDist < projList.list[pos].dist)
            {
                worstDist = projList.list[pos].dist;
                bestPos   = pos;
            }
        }

        pos++;
    }

    if (bestPos >= 0)
    {
        projList.list[bestPos].projectile = proj;
        projList.list[bestPos].location   = projPos;
        projList.list[bestPos].dist       = dist;
        projList.list[bestPos].range      = range;
    }

}


// ----------------------------------------------------------------------
// IsProjectileDangerous()
// ----------------------------------------------------------------------

function bool IsProjectileDangerous(DeusExProjectile projectile)
{
    local bool bEvil;

    if (projectile.IsA('Cloud'))
        bEvil = true;
    else if (projectile.IsA('ThrownProjectile'))
    {
        if (projectile.IsA('SpyBot'))
            bEvil = false;
        else if ((ThrownProjectile(projectile) != None) && (ThrownProjectile(projectile).bProximityTriggered))
            bEvil = false;
        else
            bEvil = true;
    }
    else
        bEvil = false;

    return (bEvil);

}


// ----------------------------------------------------------------------
// GetProjectileList()
// ----------------------------------------------------------------------

function int GetProjectileList(out NearbyProjectileList projList, vector Location)
{
    local float            dist;
    local int              count;
    local DeusExProjectile curProj;
    local ThrownProjectile throwProj;
    local Cloud            cloudProj;
    local vector           HitNormal, HitLocation;
    local vector           extent;
    local vector           traceEnd;
    local Actor            hitActor;
    local float            range;
    local vector           pos;
    local float            time;
    local float            maxTime;
    local float            elasticity;
    local int              i;
    local bool             bValid;

    for (i=0; i<ArrayCount(projList.list); i++)
        projList.list[i].projectile = None;
    projList.center = Location;

    maxTime = 2.0;
    foreach RadiusActors(Class'DeusExProjectile', curProj, 1000)
    {
        if (IsProjectileDangerous(curProj))
        {
            throwProj = ThrownProjectile(curProj);
            cloudProj = Cloud(curProj);
            extent   = vect(1,1,0)*curProj.CollisionRadius;
            extent.Z = curProj.CollisionHeight;

            range    = VSize(extent);
            if (curProj.bExplodes)
                if (range < curProj.blastRadius)
                    range = curProj.blastRadius;
            if (cloudProj != None)
                if (range < cloudProj.cloudRadius)
                    range = cloudProj.cloudRadius;
            range += CollisionRadius+60;

            if (throwProj != None)
                elasticity = throwProj.Elasticity;
            else
                elasticity = 0.2;

            bValid = true;
            if (throwProj != None)
                if (throwProj.bProximityTriggered)  // HACK!!!
                    bValid = false;

            if (((curProj.Physics == PHYS_Falling) || (curProj.Physics == PHYS_Projectile) || (curProj.Physics == PHYS_None)) &&
                bValid)
            {
                pos = curProj.Location;
                dist = VSize(Location - curProj.Location);
                AddProjectileToList(projList, curProj, pos, dist, range);
                if (curProj.Physics == PHYS_Projectile)
                {
                    traceEnd = curProj.Location + curProj.Velocity*maxTime;
                    hitActor = Trace(HitLocation, HitNormal, traceEnd, curProj.Location, true, extent);
                    if (hitActor == None)
                        pos = traceEnd;
                    else
                        pos = HitLocation;
                    dist = VSize(Location - pos);
                    AddProjectileToList(projList, curProj, pos, dist, range);
                }
                else if (curProj.Physics == PHYS_Falling)
                {
                    time = ParabolicTrace(pos, curProj.Velocity, curProj.Location, true, extent, maxTime,
                                          elasticity, curProj.bBounce, 60);
                    if (time > 0)
                    {
                        dist = VSize(Location - pos);
                        AddProjectileToList(projList, curProj, pos, dist, range);
                    }
                }
            }
        }
    }

    count = 0;
    for (i=0; i<ArrayCount(projList.list); i++)
        if (projList.list[i].projectile != None)
            count++;

    return (count);

}


// ----------------------------------------------------------------------
// IsLocationDangerous()
// ----------------------------------------------------------------------

function bool IsLocationDangerous(NearbyProjectileList projList,
                                  vector Location)
{
    local bool  bDanger;
    local int   i;
    local float dist;

    bDanger = false;
    for (i=0; i<ArrayCount(projList.list); i++)
    {
        if (projList.list[i].projectile == None)
            break;
        if (projList.center == Location)
            dist = projList.list[i].dist;
        else
            dist = VSize(projList.list[i].location - Location);
        if (dist < projList.list[i].range)
        {
            bDanger = true;
            break;
        }
    }

    return (bDanger);

}


// ----------------------------------------------------------------------
// ComputeAwayVector()
// ----------------------------------------------------------------------

function vector ComputeAwayVector(NearbyProjectileList projList)
{
    local vector          awayVect;
    local vector          tempVect;
    local rotator         tempRot;
    local int             i;
    local float           dist;
    local int             yaw;
    local int             absYaw;
    local int             bestYaw;
    local NavigationPoint navPoint;
    local NavigationPoint bestPoint;
    local float           segmentDist;

    segmentDist = GroundSpeed*0.5;

    awayVect = vect(0, 0, 0);
    for (i=0; i<ArrayCount(projList.list); i++)
    {
        if ((projList.list[i].projectile != None) &&
            (projList.list[i].dist < projList.list[i].range))
        {
            tempVect = projList.list[i].location - projList.center;
            if (projList.list[i].dist > 0)
                tempVect /= projList.list[i].dist;
            else
                tempVect = VRand();
            awayVect -= tempVect;
        }
    }

    if (awayVect != vect(0, 0, 0))
    {
        awayVect += Normal(Velocity*vect(1,1,0))*0.9;  // bias to direction already running
        yaw = Rotator(awayVect).Yaw;
        bestPoint = None;
        foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, dist)
        {
            tempRot = Rotator(navPoint.Location - Location);
            absYaw = (tempRot.Yaw - Yaw) & 65535;
            if (absYaw > 32767)
                absYaw -= 65536;
            absYaw = Abs(absYaw);
            if ((bestPoint == None) || (bestYaw > absYaw))
            {
                bestPoint = navPoint;
                bestYaw = absYaw;
            }
        }
        if (bestPoint != None)
            awayVect = bestPoint.Location-Location;
        else
        {
            tempRot = Rotator(awayVect);
            tempRot.Pitch += Rand(7282)-3641;   // +/- 20 degrees
            tempRot.Yaw   += Rand(7282)-3641;   // +/- 20 degrees
            awayVect = Vector(tempRot)*segmentDist;
        }
    }
    else
        awayVect = VRand()*segmentDist;

    return (awayVect);

}


// ----------------------------------------------------------------------
// SetupWeapon()
// ----------------------------------------------------------------------

function SetupWeapon(bool bDrawWeapon, optional bool bForce)
{
    if (bKeepWeaponDrawn && !bForce)
        bDrawWeapon = true;

    if (ShouldDropWeapon())
        DropWeapon();
    else if (bDrawWeapon)
    {
//		if (Weapon == None)
        SwitchToBestWeapon();
    }
    else
        SetWeapon(None);
}


// ----------------------------------------------------------------------
// DropWeapon()
// ----------------------------------------------------------------------

function DropWeapon()
{
    local DeusExWeapon dxWeapon;
    local Weapon       oldWeapon;

    if (Weapon != None)
    {
        dxWeapon = DeusExWeapon(Weapon);
        if ((dxWeapon == None) || !dxWeapon.bNativeAttack)
        {
            oldWeapon = Weapon;
            SetWeapon(None);
            oldWeapon.DropFrom(Location);
        }
    }
}


// ----------------------------------------------------------------------
// SetWeapon()
// ----------------------------------------------------------------------

function SetWeapon(Weapon newWeapon)
{
    if (Weapon == newWeapon)
    {
        if (Weapon != None)
        {
            if (Weapon.IsInState('DownWeapon'))
                Weapon.BringUp();
            Weapon.SetDefaultDisplayProperties();
        }
        if (Inventory != None)
            Inventory.ChangedWeapon();
        PendingWeapon = None;
        return;
    }

    PlayWeaponSwitch(newWeapon);
    if (Weapon != None)
    {
        Weapon.SetDefaultDisplayProperties();
        Weapon.PutDown();
    }

    Weapon = newWeapon;
    if (Inventory != None)
        Inventory.ChangedWeapon();
    if (Weapon != None)
        Weapon.BringUp();

    PendingWeapon = None;
}


// ----------------------------------------------------------------------
// ReactToInjury()
// ----------------------------------------------------------------------

function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
{
    local Name currentState;
    local bool bHateThisInjury;
    local bool bFearThisInjury;

    if ((health > 0) && (instigatedBy != None) && (bLookingForInjury || bLookingForIndirectInjury))
    {
        currentState = GetStateName();

        bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury);
        bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury);

        if (bHateThisInjury)
            IncreaseAgitation(instigatedBy);
        if (bFearThisInjury)
            IncreaseFear(instigatedBy, 2.0);

        if (SetEnemy(instigatedBy))
        {
            SetDistressTimer();
            SetNextState('HandlingEnemy');
        }
        else if (bFearThisInjury && IsFearful())
        {
            SetDistressTimer();
            SetEnemy(instigatedBy, , true);
            SetNextState('Fleeing');
        }
        else
        {
//			if (instigatedBy.bIsPlayer)
//				ReactToFutz();
            SetNextState(currentState);
        }
        GotoDisabledState(damageType, hitPos);
    }
}


// ----------------------------------------------------------------------
// TakeHit()
// ----------------------------------------------------------------------

function TakeHit(EHitLocation hitPos)
{
    if (hitPos != HITLOC_None)
    {
        PlayTakingHit(hitPos);
        GotoState('TakingHit');
    }
    else
        GotoNextState();
}


// ----------------------------------------------------------------------
// ComputeFallDirection()
// ----------------------------------------------------------------------

function ComputeFallDirection(float totalTime, int numFrames,
                              out vector moveDir, out float stopTime)
{
    // Determine direction, and how long to slide
    if (AnimSequence == 'DeathFront')
    {
        moveDir = Vector(DesiredRotation) * Default.CollisionRadius*0.75;
        if (numFrames > 5)
            stopTime = totalTime * ((numFrames-5)/float(numFrames));
        else
            stopTime = totalTime * 0.5;
    }
    else if (AnimSequence == 'DeathBack')
    {
        moveDir = -Vector(DesiredRotation) * Default.CollisionRadius*0.75;
        if (numFrames > 5)
            stopTime = totalTime * ((numFrames-5)/float(numFrames));
        else
            stopTime = totalTime * 0.9;
    }
}


// ----------------------------------------------------------------------
// WillTakeStompDamage()
// ----------------------------------------------------------------------

function bool WillTakeStompDamage(Actor stomper)
{
    return true;
}


// ----------------------------------------------------------------------
// DrawShield()
// ----------------------------------------------------------------------

function DrawShield()
{
    local EllipseEffect shield;

    shield = Spawn(class'EllipseEffect', Self,, Location, Rotation);
    if (shield != None)
        shield.SetBase(Self);
}


// ----------------------------------------------------------------------
// StandUp()
// ----------------------------------------------------------------------

function StandUp(optional bool bInstant)
{
    local vector placeToStand;

    if (bSitting)
    {
        bSitInterpolation = false;
        bSitting          = false;

        EnableCollision(true);
        SetBase(None);
        SetPhysics(PHYS_Falling);
        ResetBasedPawnSize();

        if (!bInstant && (SeatActor != None) && IsOverlapping(SeatActor))
        {
            bStandInterpolation = true;
            remainingStandTime  = 0.3;
            StandRate = (Vector(SeatActor.Rotation+Rot(0, -16384, 0))*CollisionRadius) /
                        remainingStandTime;
        }
        else
            StopStanding();
    }

    if (SeatActor != None)
    {
        if (SeatActor.sittingActor[seatSlot] == self)
            SeatActor.sittingActor[seatSlot] = None;
        SeatActor = None;
    }

    if (bDancing)
        bDancing = false;
}


// ----------------------------------------------------------------------
// StopStanding()
// ----------------------------------------------------------------------

function StopStanding()
{
    if (bStandInterpolation)
    {
        bStandInterpolation = false;
        remainingStandTime  = 0;
        if (Physics == PHYS_Flying)
            SetPhysics(PHYS_Falling);
    }

}


// ----------------------------------------------------------------------
// UpdateStanding()
// ----------------------------------------------------------------------

function UpdateStanding(float deltaSeconds)
{
    local float  delta;
    local vector newPos;

    if (bStandInterpolation)
    {
        if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)))  // the bastard's walking now
            StopStanding();
        else
        {
            if ((deltaSeconds < remainingStandTime) && (remainingStandTime > 0))
            {
                delta = deltaSeconds;
                remainingStandTime -= deltaSeconds;
            }
            else
            {
                delta = remainingStandTime;
                StopStanding();
            }
            newPos = StandRate*delta;
            Move(newPos);
        }
    }
}


// ----------------------------------------------------------------------
// JumpOutOfWater()
// ----------------------------------------------------------------------

function JumpOutOfWater(vector jumpDir)
{
    Falling();
    Velocity = jumpDir * WaterSpeed;
    Acceleration = jumpDir * AccelRate;
    velocity.Z = 380; //set here so physics uses this for remainder of tick
    PlayFalling();
    bUpAndOut = true;
}


// ----------------------------------------------------------------------
// SupportActor()
//
// Modified from DeusExDecoration.uc
// Called when something lands on us
// ----------------------------------------------------------------------

function SupportActor(Actor standingActor)
{
    local vector newVelocity;
    local float  angle;
    local float  zVelocity;
    local float  baseMass;
    local float  standingMass;
    local vector damagePoint;
    local float  damage;

    standingMass = FMax(1, standingActor.Mass);
    baseMass     = FMax(1, Mass);
    if ((Physics == PHYS_Swimming) && Region.Zone.bWaterZone)
    {
        newVelocity = standingActor.Velocity;
        newVelocity *= 0.5*standingMass/baseMass;
        AddVelocity(newVelocity);
    }
    else
    {
        zVelocity    = standingActor.Velocity.Z;
        damagePoint  = Location + vect(0,0,1)*(CollisionHeight-1);
        damage       = (1 - (standingMass/baseMass) * (zVelocity/100));

        // Have we been stomped?
        if ((zVelocity*standingMass < -7500) && (damage > 0) && WillTakeStompDamage(standingActor))
            TakeDamage(damage, standingActor.Instigator, damagePoint, 0.2*standingActor.Velocity, 'stomped');
    }

    // Bounce the actor off the pawn
    angle = FRand()*Pi*2;
    newVelocity.X = cos(angle);
    newVelocity.Y = sin(angle);
    newVelocity.Z = 0;
    newVelocity *= FRand()*25 + 25;
    newVelocity += standingActor.Velocity;
    newVelocity.Z = 50;
    standingActor.Velocity = newVelocity;
    standingActor.SetPhysics(PHYS_Falling);
}


// ----------------------------------------------------------------------
// SpawnCarcass()
// ----------------------------------------------------------------------

function Carcass SpawnCarcass()
{
    local DeusExCarcass carc;
    local vector loc;
    local Inventory item, nextItem;
    local FleshFragment chunk;
    local int i;
    local float size;

    // if we really got blown up good, gib us and don't display a carcass
    if ((Health < -100) && !IsA('Robot'))
    {
        size = (CollisionRadius + CollisionHeight) / 2;
        if (size > 10.0)
        {
            for (i=0; i<size/4.0; i++)
            {
                loc.X = (1-2*FRand()) * CollisionRadius;
                loc.Y = (1-2*FRand()) * CollisionRadius;
                loc.Z = (1-2*FRand()) * CollisionHeight;
                loc += Location;
                chunk = spawn(class'FleshFragment', None,, loc);
                if (chunk != None)
                {
                    chunk.DrawScale = size / 25;
                    chunk.SetCollisionSize(chunk.CollisionRadius / chunk.DrawScale, chunk.CollisionHeight / chunk.DrawScale);
                    chunk.bFixedRotationDir = True;
                    chunk.RotationRate = RotRand(False);
                }
            }
        }

        return None;
    }

    // spawn the carcass
    carc = DeusExCarcass(Spawn(CarcassType));

    if ( carc != None )
    {
        if (bStunned)
            carc.bNotDead = True;

        carc.Initfor(self);

        // move it down to the floor
        loc = Location;
        loc.z -= Default.CollisionHeight;
        loc.z += carc.Default.CollisionHeight;
        carc.SetLocation(loc);
        carc.Velocity = Velocity;
        carc.Acceleration = Acceleration;

        // give the carcass the pawn's inventory if we aren't an animal or robot
        if (!IsA('Animal') && !IsA('Robot'))
        {
            if (Inventory != None)
            {
                do
                {
                    item = Inventory;
                    nextItem = item.Inventory;
                    DeleteInventory(item);
                    if ((DeusExWeapon(item) != None) && (DeusExWeapon(item).bNativeAttack))
                        item.Destroy();
                    else
                        carc.AddInventory(item);
                    item = nextItem;
                }
                until (item == None);
            }
        }
    }

    return carc;
}


// ----------------------------------------------------------------------
// FilterDamageType()
// ----------------------------------------------------------------------

function bool FilterDamageType(Pawn instigatedBy, Vector hitLocation,
                               Vector offset, Name damageType)
{
    // Special cases for certain damage types
    if (damageType == 'HalonGas')
        if (bOnFire)
            ExtinguishFire();

    if (damageType == 'EMP')
    {
        CloakEMPTimer += 10;  // hack - replace with skill-based value
        if (CloakEMPTimer > 20)
            CloakEMPTimer = 20;
        EnableCloak(bCloakOn);
        return false;
    }

    return true;

}


// ----------------------------------------------------------------------
// ModifyDamage()
// ----------------------------------------------------------------------

function float ModifyDamage(int Damage, Pawn instigatedBy, Vector hitLocation,
                            Vector offset, Name damageType)
{
    local int   actualDamage;
    local float headOffsetZ, headOffsetY, armOffset;

    actualDamage = Damage;

    // calculate our hit extents
    headOffsetZ = CollisionHeight * 0.7;
    headOffsetY = CollisionRadius * 0.3;
    armOffset   = CollisionRadius * 0.35;

    // if the pawn is stunned, damage is 4X
    if (bStunned)
        actualDamage *= 4;

    // if the pawn is hit from behind at point-blank range, he is killed instantly
    else if (offset.x < 0)
        if ((instigatedBy != None) && (VSize(instigatedBy.Location - Location) < 64))
            actualDamage  *= 10;

    actualDamage = Level.Game.ReduceDamage(actualDamage, DamageType, self, instigatedBy);

    if (ReducedDamageType == 'All') //God mode
        actualDamage = 0;
    else if (Inventory != None) //then check if carrying armor
        actualDamage = Inventory.ReduceDamage(actualDamage, DamageType, HitLocation);

    // gas, EMP and nanovirus do no damage
    if (damageType == 'TearGas' || damageType == 'EMP' || damageType == 'NanoVirus')
        actualDamage = 0;

    return actualDamage;

}


// ----------------------------------------------------------------------
// ShieldDamage()
// ----------------------------------------------------------------------

function float ShieldDamage(Name damageType)
{
    return 1.0;
}


// ----------------------------------------------------------------------
// ImpartMomentum()
// ----------------------------------------------------------------------

function ImpartMomentum(Vector momentum, Pawn instigatedBy)
{
    if (Physics == PHYS_None)
        SetMovementPhysics();
    if (Physics == PHYS_Walking)
        momentum.Z = 0.4 * VSize(momentum);
    if ( instigatedBy == self )
        momentum *= 0.6;
    momentum = momentum/Mass;
    AddVelocity( momentum ); 
}


// ----------------------------------------------------------------------
// AddVelocity()
// ----------------------------------------------------------------------

function AddVelocity(Vector momentum)
{
    if (VSize(momentum) > 0.001)
        Super.AddVelocity(momentum);
}


// ----------------------------------------------------------------------
// CanShowPain()
// ----------------------------------------------------------------------

function bool CanShowPain()
{
    if (bShowPain && (TakeHitTimer <= 0))
        return true;
    else
        return false;
}


// ----------------------------------------------------------------------
// IsPrimaryDamageType()
// ----------------------------------------------------------------------

function bool IsPrimaryDamageType(name damageType)
{
    local bool bPrimary;

    switch (damageType)
    {
        case 'Exploded':
        case 'TearGas':
        case 'HalonGas':
        case 'PoisonGas':
        case 'PoisonEffect':
        case 'Radiation':
        case 'EMP':
        case 'Drowned':
        case 'NanoVirus':
            bPrimary = false;
            break;

        case 'Stunned':
        case 'KnockedOut':
        case 'Burned':
        case 'Flamed':
        case 'Poison':
        case 'Shot':
        case 'Sabot':
        default:
            bPrimary = true;
            break;
    }

    return (bPrimary);
}


// ----------------------------------------------------------------------
// ShouldReactToInjuryType()
// ----------------------------------------------------------------------

function bool ShouldReactToInjuryType(name damageType,
                                      bool bHatePrimary, bool bHateSecondary)
{
    local bool bIsPrimary;

    bIsPrimary = IsPrimaryDamageType(damageType);
    if ((bHatePrimary && bIsPrimary) || (bHateSecondary && !bIsPrimary))
        return true;
    else
        return false;
}


// ----------------------------------------------------------------------
// HandleDamage()
// ----------------------------------------------------------------------

function EHitLocation HandleDamage(int actualDamage, Vector hitLocation, Vector offset, name damageType)
{
    local EHitLocation hitPos;
    local float        headOffsetZ, headOffsetY, armOffset;

    // calculate our hit extents
    headOffsetZ = CollisionHeight * 0.7;
    headOffsetY = CollisionRadius * 0.3;
    armOffset   = CollisionRadius * 0.35;

    hitPos = HITLOC_None;

    if (actualDamage > 0)
    {
        if (offset.z > headOffsetZ)     // head
        {
            // narrow the head region
            if ((Abs(offset.x) < headOffsetY) || (Abs(offset.y) < headOffsetY))
            {
                // don't allow headshots with stunning weapons
                if ((damageType == 'Stunned') || (damageType == 'KnockedOut'))
                    HealthHead -= actualDamage;
                else
                    HealthHead -= actualDamage * 8;
                if (offset.x < 0.0)
                    hitPos = HITLOC_HeadBack;
                else
                    hitPos = HITLOC_HeadFront;
            }
            else  // sides of head treated as torso
            {
                HealthTorso -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_TorsoBack;
                else
                    hitPos = HITLOC_TorsoFront;
            }
        }
        else if (offset.z < 0.0)    // legs
        {
            if (offset.y > 0.0)
            {
                HealthLegRight -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_RightLegBack;
                else
                    hitPos = HITLOC_RightLegFront;
            }
            else
            {
                HealthLegLeft -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_LeftLegBack;
                else
                    hitPos = HITLOC_LeftLegFront;
            }

            // if this part is already dead, damage the adjacent part
            if ((HealthLegRight < 0) && (HealthLegLeft > 0))
            {
                HealthLegLeft += HealthLegRight;
                HealthLegRight = 0;
            }
            else if ((HealthLegLeft < 0) && (HealthLegRight > 0))
            {
                HealthLegRight += HealthLegLeft;
                HealthLegLeft = 0;
            }

            if (HealthLegLeft < 0)
            {
                HealthTorso += HealthLegLeft;
                HealthLegLeft = 0;
            }
            if (HealthLegRight < 0)
            {
                HealthTorso += HealthLegRight;
                HealthLegRight = 0;
            }
        }
        else                        // arms and torso
        {
            if (offset.y > armOffset)
            {
                HealthArmRight -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_RightArmBack;
                else
                    hitPos = HITLOC_RightArmFront;
            }
            else if (offset.y < -armOffset)
            {
                HealthArmLeft -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_LeftArmBack;
                else
                    hitPos = HITLOC_LeftArmFront;
            }
            else
            {
                HealthTorso -= actualDamage * 2;
                if (offset.x < 0.0)
                    hitPos = HITLOC_TorsoBack;
                else
                    hitPos = HITLOC_TorsoFront;
            }

            // if this part is already dead, damage the adjacent part
            if (HealthArmLeft < 0)
            {
                HealthTorso += HealthArmLeft;
                HealthArmLeft = 0;
            }
            if (HealthArmRight < 0)
            {
                HealthTorso += HealthArmRight;
                HealthArmRight = 0;
            }
        }
    }

    GenerateTotalHealth();

    return hitPos;

}


// ----------------------------------------------------------------------
// TakeDamageBase()
// ----------------------------------------------------------------------

function TakeDamageBase(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType,
                        bool bPlayAnim)
{
    local int          actualDamage;
    local Vector       offset;
    local float        origHealth;
    local EHitLocation hitPos;
    local float        shieldMult;

    // use the hitlocation to determine where the pawn is hit
    // transform the worldspace hitlocation into objectspace
    // in objectspace, remember X is front to back
    // Y is side to side, and Z is top to bottom
    offset = (hitLocation - Location) << Rotation;

    if (!CanShowPain())
        bPlayAnim = false;

    // Prevent injury if the NPC is intangible
    if (!bBlockActors && !bBlockPlayers && !bCollideActors)
        return;

    // No damage + no damage type = no reaction
    if ((Damage <= 0) && (damageType == 'None'))
        return;

    // Block certain damage types; perform special ops on others
    if (!FilterDamageType(instigatedBy, hitLocation, offset, damageType))
        return;

    // Impart momentum
    ImpartMomentum(momentum, instigatedBy);

    actualDamage = ModifyDamage(Damage, instigatedBy, hitLocation, offset, damageType);

    if (actualDamage > 0)
    {
        shieldMult = ShieldDamage(damageType);
        if (shieldMult > 0)
            actualDamage = Max(int(actualDamage*shieldMult), 1);
        else
            actualDamage = 0;
        if (shieldMult < 1.0)
            DrawShield();
    }

    origHealth = Health;

    hitPos = HandleDamage(actualDamage, hitLocation, offset, damageType);
    if (!bPlayAnim || (actualDamage <= 0))
        hitPos = HITLOC_None;

    if (bCanBleed)
        if ((damageType != 'Stunned') && (damageType != 'TearGas') && (damageType != 'HalonGas') &&
            (damageType != 'PoisonGas') && (damageType != 'Radiation') && (damageType != 'EMP') &&
            (damageType != 'NanoVirus') && (damageType != 'Drowned') && (damageType != 'KnockedOut') &&
            (damageType != 'Poison') && (damageType != 'PoisonEffect'))
            bleedRate += (origHealth-Health)/(0.3*Default.Health);  // 1/3 of default health = bleed profusely

    if (CarriedDecoration != None)
        DropDecoration();

    if ((actualDamage > 0) && (damageType == 'Poison'))
        StartPoison(Damage, instigatedBy);

    if (Health <= 0)
    {
        ClearNextState();
        //PlayDeathHit(actualDamage, hitLocation, damageType);
        if ( actualDamage > mass )
            Health = -1 * actualDamage;
        SetEnemy(instigatedBy, 0, true);

        // gib us if we get blown up
        if (DamageType == 'Exploded')
            Health = -10000;
        else
            Health = -1;

        Died(instigatedBy, damageType, HitLocation);

        if ((DamageType == 'Flamed') || (DamageType == 'Burned'))
        {
            bBurnedToDeath = true;
            ScaleGlow *= 0.1;  // blacken the corpse
        }
        else
            bBurnedToDeath = false;

        return;
    }

    // play a hit sound
    if (DamageType != 'Stunned')
        PlayTakeHitSound(actualDamage, damageType, 1);

    if ((DamageType == 'Flamed') && !bOnFire)
        CatchFire();

    ReactToInjury(instigatedBy, damageType, hitPos);
}


// ----------------------------------------------------------------------
// IsNearHome()
// ----------------------------------------------------------------------

function bool IsNearHome(vector position)
{
    local bool bNear;

    bNear = true;
    if (bUseHome)
    {
        if (VSize(HomeLoc-position) <= HomeExtent)
        {
            if (!FastTrace(position, HomeLoc))
                bNear = false;
        }
        else
            bNear = false;
    }

    return bNear;
}


// ----------------------------------------------------------------------
// IsDoor()
// ----------------------------------------------------------------------

function bool IsDoor(Actor door, optional bool bWarn)
{
    local bool        bIsDoor;
    local DeusExMover dxMover;

    bIsDoor = false;

    dxMover = DeusExMover(door);
    if (dxMover != None)
    {
        if (dxMover.NumKeys > 1)
        {
            if (dxMover.bIsDoor)
                bIsDoor = true;
            /*
            else if (bWarn)  // hack for now
                log("WARNING: NPC "$self$" trying to use door "$dxMover$", but bIsDoor flag is False");
            */
        }
    }

    return bIsDoor;
}


// ----------------------------------------------------------------------
// CheckOpenDoor()
// ----------------------------------------------------------------------

function CheckOpenDoor(vector HitNormal, actor Door, optional name Label)
{
    local DeusExMover dxMover;

    dxMover = DeusExMover(Door);
    if (dxMover != None)
    {
        if (bCanOpenDoors && !IsDoor(dxMover) && dxMover.bBreakable)  // break glass we walk into
        {
            dxMover.TakeDamage(200, self, dxMover.Location, Velocity, 'Shot');
            return;
        }

        if (dxMover.bInterpolating && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach))
            return;

        if (bCanOpenDoors && bInterruptState && !bInTransientState && IsDoor(dxMover, true))
        {
            if (Label == '')
                Label = 'Begin';
            if (GetStateName() != 'OpeningDoor')
                SetNextState(GetStateName(), 'ContinueFromDoor');
            Target = Door;
            destLoc = HitNormal;
            GotoState('OpeningDoor', 'BeginHitNormal');
        }
        else if ((Acceleration != vect(0,0,0)) && (Physics == PHYS_Walking) &&
                 (TurnDirection == TURNING_None))
            Destination = Location;
    }
}


// ----------------------------------------------------------------------
// EncroachedBy()
// ----------------------------------------------------------------------

event EncroachedBy( actor Other )
{
    // overridden so indestructable NPCs aren't InstaGibbed by stupid movement code
}


// ----------------------------------------------------------------------
// EncroachedByMover()
// ----------------------------------------------------------------------

function EncroachedByMover(Mover encroacher)
{
    local DeusExMover dxMover;

    dxMover = DeusExMover(encroacher);
    if (dxMover != None)
        if (!dxMover.bInterpolating && IsDoor(dxMover))
            FrobDoor(dxMover);
}


// ----------------------------------------------------------------------
// FrobDoor()
// ----------------------------------------------------------------------

function bool FrobDoor(actor Target)
{
    local DeusExMover      dxMover;
    local DeusExMover      triggerMover;
    local DeusExDecoration trigger;
    local float            dist;
    local DeusExDecoration bestTrigger;
    local float            bestDist;
    local bool             bDone;

    bDone = false;

    dxMover = DeusExMover(Target);
    if (dxMover != None)
    {
        bestTrigger = None;
        bestDist    = 10000;
        foreach AllActors(Class'DeusExDecoration', trigger)
        {
            if (dxMover.Tag == trigger.Event)
            {
                dist = VSize(Location - trigger.Location);
                if ((bestTrigger == None) || (bestDist > dist))
                {
                    bestTrigger = trigger;
                    bestDist    = dist;
                }
            }
        }
        if (bestTrigger != None)
        {
            foreach AllActors(Class'DeusExMover', triggerMover, dxMover.Tag)
                triggerMover.Trigger(bestTrigger, self);
            bDone = true;
        }
        else if (dxMover.bFrobbable)
        {
            if ((dxMover.WaitingPawn == None) ||
                (dxMover.WaitingPawn == self))
            {
                dxMover.Frob(self, None);
                bDone = true;
            }
        }

        if (bDone)
            dxMover.WaitingPawn = self;
    }
    return bDone;

}


// ----------------------------------------------------------------------
// GotoDisabledState()
// ----------------------------------------------------------------------

function GotoDisabledState(name damageType, EHitLocation hitPos)
{
    if (!bCollideActors && !bBlockActors && !bBlockPlayers)
        return;
    else if ((damageType == 'TearGas') || (damageType == 'HalonGas'))
        GotoState('RubbingEyes');
    else if (damageType == 'Stunned')
        GotoState('Stunned');
    else if (CanShowPain())
        TakeHit(hitPos);
    else
        GotoNextState();
}


// ----------------------------------------------------------------------
// PlayAnimPivot()
// ----------------------------------------------------------------------

function PlayAnimPivot(name Sequence, optional float Rate, optional float TweenTime,
                       optional vector NewPrePivot)
{
    if (Rate == 0)
        Rate = 1.0;
    if (TweenTime == 0)
        TweenTime = 0.1;
    PlayAnim(Sequence, Rate, TweenTime);
    PrePivotTime    = TweenTime;
    DesiredPrePivot = NewPrePivot + PrePivotOffset;
    if (PrePivotTime <= 0)
        PrePivot = DesiredPrePivot;
}


// ----------------------------------------------------------------------
// LoopAnimPivot()
// ----------------------------------------------------------------------

function LoopAnimPivot(name Sequence, optional float Rate, optional float TweenTime, optional float MinRate,
                       optional vector NewPrePivot)
{
    if (Rate == 0)
        Rate = 1.0;
    if (TweenTime == 0)
        TweenTime = 0.1;
    LoopAnim(Sequence, Rate, TweenTime, MinRate);
    PrePivotTime    = TweenTime;
    DesiredPrePivot = NewPrePivot + PrePivotOffset;
    if (PrePivotTime <= 0)
        PrePivot = DesiredPrePivot;
}


// ----------------------------------------------------------------------
// TweenAnimPivot()
// ----------------------------------------------------------------------

function TweenAnimPivot(name Sequence, float TweenTime,
                        optional vector NewPrePivot)
{
    if (TweenTime == 0)
        TweenTime = 0.1;
    TweenAnim(Sequence, TweenTime);
    PrePivotTime    = TweenTime;
    DesiredPrePivot = NewPrePivot + PrePivotOffset;
    if (PrePivotTime <= 0)
        PrePivot = DesiredPrePivot;
}


// ----------------------------------------------------------------------
// HasTwoHandedWeapon()
// ----------------------------------------------------------------------

function Bool HasTwoHandedWeapon()
{
    if ((Weapon != None) && (Weapon.Mass >= 30))
        return True;
    else
        return False;
}


// ----------------------------------------------------------------------
// GetStyleTexture()
// ----------------------------------------------------------------------

function Texture GetStyleTexture(ERenderStyle newStyle, texture oldTex, optional texture newTex)
{
    local texture defaultTex;

    if      (newStyle == STY_Translucent)
        defaultTex = Texture'BlackMaskTex';
    else if (newStyle == STY_Modulated)
        defaultTex = Texture'GrayMaskTex';
    else if (newStyle == STY_Masked)
        defaultTex = Texture'PinkMaskTex';
    else
        defaultTex = Texture'BlackMaskTex';

    if (oldTex == None)
        return defaultTex;
    else if (oldTex == Texture'BlackMaskTex')
        return Texture'BlackMaskTex';  // hack
    else if (oldTex == Texture'GrayMaskTex')
        return defaultTex;
    else if (oldTex == Texture'PinkMaskTex')
        return defaultTex;
    else if (newTex != None)
        return newTex;
    else
        return oldTex;

}


// ----------------------------------------------------------------------
// SetSkinStyle()
// ----------------------------------------------------------------------

function SetSkinStyle(ERenderStyle newStyle, optional texture newTex, optional float newScaleGlow)
{
    local int     i;
    local texture curSkin;
    local texture oldSkin;

    if (newScaleGlow == 0)
        newScaleGlow = ScaleGlow;

    oldSkin = Skin;
    for (i=0; i<8; i++)
    {
        curSkin = GetMeshTexture(i);
        MultiSkins[i] = GetStyleTexture(newStyle, curSkin, newTex);
    }
    Skin      = GetStyleTexture(newStyle, Skin, newTex);
    ScaleGlow = newScaleGlow;
    Style     = newStyle;
}


// ----------------------------------------------------------------------
// ResetSkinStyle()
// ----------------------------------------------------------------------

function ResetSkinStyle()
{
    local int i;

    for (i=0; i<8; i++)
        MultiSkins[i] = Default.MultiSkins[i];
    Skin      = Default.Skin;
    ScaleGlow = Default.ScaleGlow;
    Style     = Default.Style;
}


// ----------------------------------------------------------------------
// EnableCloak()
// ----------------------------------------------------------------------

function EnableCloak(bool bEnable)  // beware! called from C++
{
    if (!bHasCloak || (CloakEMPTimer > 0) || (Health <= 0) || bOnFire)
        bEnable = false;

    if (bEnable && !bCloakOn)
    {
        SetSkinStyle(STY_Translucent, Texture'WhiteStatic', 0.05);
        KillShadow();
        bCloakOn = bEnable;
    }
    else if (!bEnable && bCloakOn)
    {
        ResetSkinStyle();
        CreateShadow();
        bCloakOn = bEnable;
    }
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// SOUND FUNCTIONS
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// PlayBodyThud()
// ----------------------------------------------------------------------

function PlayBodyThud()
{
    PlaySound(sound'BodyThud', SLOT_Interact);
}


// ----------------------------------------------------------------------
// RandomPitch()
//
// Repetitive sound pitch randomizer to help make some sounds
// sound less monotonous
// ----------------------------------------------------------------------

function float RandomPitch()
{
    return (1.1 - 0.2*FRand());
}


// ----------------------------------------------------------------------
// Gasp()
// ----------------------------------------------------------------------

function Gasp()
{
    PlaySound(sound'MaleGasp', SLOT_Pain,,,, RandomPitch());
}


// ----------------------------------------------------------------------
// PlayDyingSound()
// ----------------------------------------------------------------------

function PlayDyingSound()
{
    SetDistressTimer();
    PlaySound(Die, SLOT_Pain,,,, RandomPitch());
    AISendEvent('LoudNoise', EAITYPE_Audio);
    if (bEmitDistress)
        AISendEvent('Distress', EAITYPE_Audio);
}


// ----------------------------------------------------------------------
// PlayIdleSound()
// ----------------------------------------------------------------------

function PlayIdleSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
        dxPlayer.StartAIBarkConversation(self, BM_Idle);
}


// ----------------------------------------------------------------------
// PlayScanningSound()
// ----------------------------------------------------------------------

function PlayScanningSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
        dxPlayer.StartAIBarkConversation(self, BM_Scanning);
}


// ----------------------------------------------------------------------
// PlayPreAttackSearchingSound()
// ----------------------------------------------------------------------

function PlayPreAttackSearchingSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_PreAttackSearching);
}


// ----------------------------------------------------------------------
// PlayPreAttackSightingSound()
// ----------------------------------------------------------------------

function PlayPreAttackSightingSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_PreAttackSighting);
}


// ----------------------------------------------------------------------
// PlayPostAttackSearchingSound()
// ----------------------------------------------------------------------

function PlayPostAttackSearchingSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_PostAttackSearching);
}


// ----------------------------------------------------------------------
// PlayTargetAcquiredSound()
// ----------------------------------------------------------------------

function PlayTargetAcquiredSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_TargetAcquired);
}


// ----------------------------------------------------------------------
// PlayTargetLostSound()
// ----------------------------------------------------------------------

function PlayTargetLostSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_TargetLost);
}


// ----------------------------------------------------------------------
// PlaySearchGiveUpSound()
// ----------------------------------------------------------------------

function PlaySearchGiveUpSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_SearchGiveUp);
}


// ----------------------------------------------------------------------
// PlayNewTargetSound()
// ----------------------------------------------------------------------

function PlayNewTargetSound()
{
    // someday...
}


// ----------------------------------------------------------------------
// PlayGoingForAlarmSound()
// ----------------------------------------------------------------------

function PlayGoingForAlarmSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_GoingForAlarm);
}


// ----------------------------------------------------------------------
// PlayOutOfAmmoSound()
// ----------------------------------------------------------------------

function PlayOutOfAmmoSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
        dxPlayer.StartAIBarkConversation(self, BM_OutOfAmmo);
}


// ----------------------------------------------------------------------
// PlayCriticalDamageSound()
// ----------------------------------------------------------------------

function PlayCriticalDamageSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_CriticalDamage);
}


// ----------------------------------------------------------------------
// PlayAreaSecureSound()
// ----------------------------------------------------------------------

function PlayAreaSecureSound()
{
    local DeusExPlayer dxPlayer;

    // Should we do a player check here?

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_AreaSecure);
}


// ----------------------------------------------------------------------
// PlayFutzSound()
// ----------------------------------------------------------------------

function PlayFutzSound()
{
    local DeusExPlayer dxPlayer;
    local Name         conName;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
    {
        if (dxPlayer.barkManager != None)
        {
            conName = dxPlayer.barkManager.BuildBarkName(self, BM_Futz);
            dxPlayer.StartConversationByName(conName, self, !bInterruptState);
        }
//		dxPlayer.StartAIBarkConversation(self, BM_Futz);
    }
}


// ----------------------------------------------------------------------
// PlayOnFireSound()
// ----------------------------------------------------------------------

function PlayOnFireSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
        dxPlayer.StartAIBarkConversation(self, BM_OnFire);
}


// ----------------------------------------------------------------------
// PlayTearGasSound()
// ----------------------------------------------------------------------

function PlayTearGasSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if (dxPlayer != None)
        dxPlayer.StartAIBarkConversation(self, BM_TearGas);
}


// ----------------------------------------------------------------------
// PlayCarcassSound()
// ----------------------------------------------------------------------

function PlayCarcassSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (SeekPawn == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_Gore);
}


// ----------------------------------------------------------------------
// PlaySurpriseSound()
// ----------------------------------------------------------------------

function PlaySurpriseSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_Surprise);
}


// ----------------------------------------------------------------------
// PlayAllianceHostileSound()
// ----------------------------------------------------------------------

function PlayAllianceHostileSound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_AllianceHostile);
}


// ----------------------------------------------------------------------
// PlayAllianceFriendlySound()
// ----------------------------------------------------------------------

function PlayAllianceFriendlySound()
{
    local DeusExPlayer dxPlayer;

    dxPlayer = DeusExPlayer(GetPlayerPawn());
    if ((dxPlayer != None) && (Enemy == dxPlayer))
        dxPlayer.StartAIBarkConversation(self, BM_AllianceFriendly);
}


// ----------------------------------------------------------------------
// PlayTakeHitSound()
// ----------------------------------------------------------------------

function PlayTakeHitSound(int Damage, name damageType, int Mult)
{
    local Sound hitSound;
    local float volume;

    if (Level.TimeSeconds - LastPainSound < 0.25)
        return;
    if (Damage <= 0)
        return;

    LastPainSound = Level.TimeSeconds;

    if (Damage <= 30)
        hitSound = HitSound1;
    else
        hitSound = HitSound2;
    volume = FMax(Mult*TransientSoundVolume, Mult*2.0);

    SetDistressTimer();
    PlaySound(hitSound, SLOT_Pain, volume,,, RandomPitch());
    if ((hitSound != None) && bEmitDistress)
        AISendEvent('Distress', EAITYPE_Audio, volume);
}


// ----------------------------------------------------------------------
// GetFloorMaterial()
//
// Gets the name of the texture group that we are standing on
// ----------------------------------------------------------------------

function name GetFloorMaterial()
{
    local vector EndTrace, HitLocation, HitNormal;
    local actor target;
    local int texFlags;
    local name texName, texGroup;

    // trace down to our feet
    EndTrace = Location - CollisionHeight * 2 * vect(0,0,1);

    foreach TraceTexture(class'Actor', target, texName, texGroup, texFlags, HitLocation, HitNormal, EndTrace)
    {
        if ((target == Level) || target.IsA('Mover'))
            break;
    }

    return texGroup;
}


// ----------------------------------------------------------------------
// PlayFootStep()
//
// Plays footstep sounds based on the texture group
// (yes, I know this looks nasty -- I'll have to figure out a cleaner way to do this)
// ----------------------------------------------------------------------

function PlayFootStep()
{
    local Sound stepSound;
    local float rnd;
    local name mat;
    local float speedFactor, massFactor;
    local float volume, pitch, range;
    local float radius, maxRadius;
    local float volumeMultiplier;

    local DeusExPlayer dxPlayer;
    local float shakeRadius, shakeMagnitude;
    local float playerDist;

    rnd = FRand();
    mat = GetFloorMaterial();

    volumeMultiplier = 1.0;
    if (WalkSound == None)
    {
        if (FootRegion.Zone.bWaterZone)
        {
            if (rnd < 0.33)
                stepSound = Sound'WaterStep1';
            else if (rnd < 0.66)
                stepSound = Sound'WaterStep2';
            else
                stepSound = Sound'WaterStep3';
        }
        else
        {
            switch(mat)
            {
                case 'Textile':
                case 'Paper':
                    volumeMultiplier = 0.7;
                    if (rnd < 0.25)
                        stepSound = Sound'CarpetStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'CarpetStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'CarpetStep3';
                    else
                        stepSound = Sound'CarpetStep4';
                    break;

                case 'Foliage':
                case 'Earth':
                    volumeMultiplier = 0.6;
                    if (rnd < 0.25)
                        stepSound = Sound'GrassStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'GrassStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'GrassStep3';
                    else
                        stepSound = Sound'GrassStep4';
                    break;

                case 'Metal':
                case 'Ladder':
                    volumeMultiplier = 1.0;
                    if (rnd < 0.25)
                        stepSound = Sound'MetalStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'MetalStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'MetalStep3';
                    else
                        stepSound = Sound'MetalStep4';
                    break;

                case 'Ceramic':
                case 'Glass':
                case 'Tiles':
                    volumeMultiplier = 0.7;
                    if (rnd < 0.25)
                        stepSound = Sound'TileStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'TileStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'TileStep3';
                    else
                        stepSound = Sound'TileStep4';
                    break;

                case 'Wood':
                    volumeMultiplier = 0.7;
                    if (rnd < 0.25)
                        stepSound = Sound'WoodStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'WoodStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'WoodStep3';
                    else
                        stepSound = Sound'WoodStep4';
                    break;

                case 'Brick':
                case 'Concrete':
                case 'Stone':
                case 'Stucco':
                default:
                    volumeMultiplier = 0.7;
                    if (rnd < 0.25)
                        stepSound = Sound'StoneStep1';
                    else if (rnd < 0.5)
                        stepSound = Sound'StoneStep2';
                    else if (rnd < 0.75)
                        stepSound = Sound'StoneStep3';
                    else
                        stepSound = Sound'StoneStep4';
                    break;
            }
        }
    }
    else
        stepSound = WalkSound;

    // compute sound volume, range and pitch, based on mass and speed
    speedFactor = VSize(Velocity)/120.0;
    massFactor  = Mass/150.0;
    radius      = 768.0;
    maxRadius   = 2048.0;
//	volume      = (speedFactor+0.2)*massFactor;
//	volume      = (speedFactor+0.7)*massFactor;
    volume      = massFactor*1.5;
    range       = radius * volume;
    pitch       = (volume+0.5);
    volume      = 1.0;
    range       = FClamp(range, 0.01, maxRadius);
    pitch       = FClamp(pitch, 1.0, 1.5);

    // play the sound and send an AI event
    PlaySound(stepSound, SLOT_Interact, volume, , range, pitch);
    AISendEvent('LoudNoise', EAITYPE_Audio, volume*volumeMultiplier, range*volumeMultiplier);

    // Shake the camera when heavy things tread
    if (Mass > 400)
    {
        dxPlayer = DeusExPlayer(GetPlayerPawn());
        if (dxPlayer != None)
        {
            playerDist = DistanceFromPlayer;
            shakeRadius = FClamp((Mass-400)/600, 0, 1.0) * (range*0.5);
            shakeMagnitude = FClamp((Mass-400)/1600, 0, 1.0);
            shakeMagnitude = FClamp(1.0-(playerDist/shakeRadius), 0, 1.0) * shakeMagnitude;
            if (shakeMagnitude > 0)
                dxPlayer.JoltView(shakeMagnitude);
        }
    }
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ANIMATION CALLS
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// GetSwimPivot()
// ----------------------------------------------------------------------

function vector GetSwimPivot()
{
    // THIS IS A HIDEOUS, UGLY, MASSIVELY EVIL HACK!!!!
    return (vect(0,0,1)*CollisionHeight*0.65);
}


// ----------------------------------------------------------------------
// GetWalkingSpeed()
// ----------------------------------------------------------------------

function float GetWalkingSpeed()
{
    if (Physics == PHYS_Swimming)
        return MaxDesiredSpeed;
    else
        return WalkingSpeed;
}


// ----------------------------------------------------------------------
// PlayTurnHead()
// ----------------------------------------------------------------------

function bool PlayTurnHead(ELookDirection newLookDir, float rate, float tweentime)
{
    if (bCanTurnHead)
    {
        if (Super.PlayTurnHead(newLookDir, rate, tweentime))
        {
            AIAddViewRotation = rot(0,0,0); // default
            switch (newLookDir)
            {
                case LOOK_Left:
                    AIAddViewRotation = rot(0,-5461,0);  // 30 degrees left
                    break;
                case LOOK_Right:
                    AIAddViewRotation = rot(0,5461,0);   // 30 degrees right
                    break;
                case LOOK_Up:
                    AIAddViewRotation = rot(5461,0,0);   // 30 degrees up
                    break;
                case LOOK_Down:
                    AIAddViewRotation = rot(-5461,0,0);  // 30 degrees down
                    break;

                case LOOK_Forward:
                    AIAddViewRotation = rot(0,0,0);      // 0 degrees
                    break;
            }
        }
        else
            return false;
    }
    else
        return false;
}


// ----------------------------------------------------------------------
// PlayRunningAndFiring()
// ----------------------------------------------------------------------

function PlayRunningAndFiring()
{
    local DeusExWeapon W;
    local vector       v1, v2;
    local float        dotp;

    bIsWalking = FALSE;

    W = DeusExWeapon(Weapon);

    if (W != None)
    {
        if (Region.Zone.bWaterZone)
        {
            if (W.bHandToHand)
                LoopAnimPivot('Tread',,0.1,,GetSwimPivot());
            else
                LoopAnimPivot('TreadShoot',,0.1,,GetSwimPivot());
        }
        else
        {
            if (W.bHandToHand)
                LoopAnimPivot('Run',runAnimMult,0.1);
            else
            {
                v1 = Normal((Enemy.Location - Location)*vect(1,1,0));
                if (destPoint != None)
                    v2 = Normal((destPoint.Location - Location)*vect(1,1,0));
                else
                    v2 = Normal((destLoc - Location)*vect(1,1,0));
                dotp = Abs(v1 dot v2);
                if (dotp < 0.70710678)  // running sideways
                {
                    if (HasTwoHandedWeapon())
                        LoopAnimPivot('Strafe2H',runAnimMult,0.1);
                    else
                        LoopAnimPivot('Strafe',runAnimMult,0.1);
                }
                else
                {
                    if (HasTwoHandedWeapon())
                        LoopAnimPivot('RunShoot2H',runAnimMult,0.1);
                    else
                        LoopAnimPivot('RunShoot',runAnimMult,0.1);
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// PlayReloadBegin()
// ----------------------------------------------------------------------

function PlayReloadBegin()
{
    PlayAnimPivot('ReloadBegin',, 0.1);
}


// ----------------------------------------------------------------------
// PlayReload()
// ----------------------------------------------------------------------

function PlayReload()
{
    LoopAnimPivot('Reload',,0.2);
}


// ----------------------------------------------------------------------
// PlayReloadEnd()
// ----------------------------------------------------------------------

function PlayReloadEnd()
{
    PlayAnimPivot('ReloadEnd',, 0.1);
}


// ----------------------------------------------------------------------
// TweenToShoot()
// ----------------------------------------------------------------------

function TweenToShoot(float tweentime)
{
    if (Region.Zone.bWaterZone)
        TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot());
    else if (!bCrouching)
    {
        if (!IsWeaponReloading())
        {
            if (HasTwoHandedWeapon())
                TweenAnimPivot('Shoot2H', tweentime);
            else
                TweenAnimPivot('Shoot', tweentime);
        }
        else
            PlayReload();
    }
}


// ----------------------------------------------------------------------
// PlayShoot()
// ----------------------------------------------------------------------

function PlayShoot()
{
    if (Region.Zone.bWaterZone)
        PlayAnimPivot('TreadShoot', , 0, GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            PlayAnimPivot('Shoot2H', , 0);
        else
            PlayAnimPivot('Shoot', , 0);
    }
}


// ----------------------------------------------------------------------
// TweenToCrouchShoot()
// ----------------------------------------------------------------------

function TweenToCrouchShoot(float tweentime)
{
    if (Region.Zone.bWaterZone)
        TweenAnimPivot('TreadShoot', tweentime, GetSwimPivot());
    else
        TweenAnimPivot('CrouchShoot', tweentime);
}


// ----------------------------------------------------------------------
// PlayCrouchShoot()
// ----------------------------------------------------------------------

function PlayCrouchShoot()
{
    if (Region.Zone.bWaterZone)
        PlayAnimPivot('TreadShoot', , 0, GetSwimPivot());
    else
        PlayAnimPivot('CrouchShoot', , 0);
}


// ----------------------------------------------------------------------
// TweenToAttack()
// ----------------------------------------------------------------------

function TweenToAttack(float tweentime)
{
    if (Region.Zone.bWaterZone)
        TweenAnimPivot('Tread', tweentime, GetSwimPivot());
    else
    {
        if (bUseSecondaryAttack)
            TweenAnimPivot('AttackSide', tweentime);
        else
            TweenAnimPivot('Attack', tweentime);
    }
}


// ----------------------------------------------------------------------
// PlayAttack()
// ----------------------------------------------------------------------

function PlayAttack()
{
    if (Region.Zone.bWaterZone)
        PlayAnimPivot('Tread',,,GetSwimPivot());
    else
    {
        if (bUseSecondaryAttack)
            PlayAnimPivot('AttackSide');
        else
            PlayAnimPivot('Attack');
    }
}


// ----------------------------------------------------------------------
// PlayTurning()
// ----------------------------------------------------------------------

function PlayTurning()
{
//	ClientMessage("PlayTurning()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread', , , , GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            TweenAnimPivot('Walk2H', 0.1);
        else
            TweenAnimPivot('Walk', 0.1);
    }
}


// ----------------------------------------------------------------------
// TweenToWalking()
// ----------------------------------------------------------------------

function TweenToWalking(float tweentime)
{
//	ClientMessage("TweenToWalking()");
    bIsWalking = True;
    if (Region.Zone.bWaterZone)
        TweenAnimPivot('Tread', tweentime, GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            TweenAnimPivot('Walk2H', tweentime);
        else
            TweenAnimPivot('Walk', tweentime);
    }
}


// ----------------------------------------------------------------------
// PlayWalking()
// ----------------------------------------------------------------------

function PlayWalking()
{
//	ClientMessage("PlayWalking()");
    bIsWalking = True;
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread', , 0.15, , GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            LoopAnimPivot('Walk2H',walkAnimMult, 0.15);
        else
            LoopAnimPivot('Walk',walkAnimMult, 0.15);
    }
}


// ----------------------------------------------------------------------
// TweenToRunning()
// ----------------------------------------------------------------------

function TweenToRunning(float tweentime)
{
//	ClientMessage("TweenToRunning()");
    bIsWalking = False;
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',, tweentime,, GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            LoopAnimPivot('RunShoot2H', runAnimMult, tweentime);
        else
            LoopAnimPivot('Run', runAnimMult, tweentime);
    }
}


// ----------------------------------------------------------------------
// PlayRunning()
// ----------------------------------------------------------------------

function PlayRunning()
{
//	ClientMessage("PlayRunning()");
    bIsWalking = False;
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',,,,GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            LoopAnimPivot('RunShoot2H', runAnimMult);
        else
            LoopAnimPivot('Run', runAnimMult);
    }
}


// ----------------------------------------------------------------------
// PlayPanicRunning()
// ----------------------------------------------------------------------

function PlayPanicRunning()
{
//	ClientMessage("PlayPanicRunning()");
    bIsWalking = False;
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',,,,GetSwimPivot());
    else
        LoopAnimPivot('Panic', runAnimMult);
}


// ----------------------------------------------------------------------
// TweenToWaiting()
// ----------------------------------------------------------------------

function TweenToWaiting(float tweentime)
{
//	ClientMessage("TweenToWaiting()");
    if (Region.Zone.bWaterZone)
        TweenAnimPivot('Tread', tweentime, GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            TweenAnimPivot('BreatheLight2H', tweentime);
        else
            TweenAnimPivot('BreatheLight', tweentime);
    }
}


// ----------------------------------------------------------------------
// PlayWaiting()
// ----------------------------------------------------------------------

function PlayWaiting()
{
//	ClientMessage("PlayWaiting()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread', , 0.3, , GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            LoopAnimPivot('BreatheLight2H', , 0.3);
        else
            LoopAnimPivot('BreatheLight', , 0.3);
    }
}


// ----------------------------------------------------------------------
// PlayIdle()
// ----------------------------------------------------------------------

function PlayIdle()
{
//	ClientMessage("PlayIdle()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread', , 0.3, , GetSwimPivot());
    else
    {
        if (HasTwoHandedWeapon())
            PlayAnimPivot('Idle12H', , 0.3);
        else
            PlayAnimPivot('Idle1', , 0.3);
    }
}


// ----------------------------------------------------------------------
// PlayDancing()
// ----------------------------------------------------------------------

function PlayDancing()
{
//	ClientMessage("PlayDancing()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread', , 0.3, , GetSwimPivot());
    else
        LoopAnimPivot('Dance', FRand()*0.2+0.9, 0.3);
}


// ----------------------------------------------------------------------
// PlaySittingDown()
// ----------------------------------------------------------------------

function PlaySittingDown()
{
//	ClientMessage("PlaySittingDown()");
    PlayAnimPivot('SitBegin', , 0.15);
}


// ----------------------------------------------------------------------
// PlaySitting()
// ----------------------------------------------------------------------

function PlaySitting()
{
//	ClientMessage("PlaySitting()");
    LoopAnimPivot('SitBreathe', , 0.15);
}


// ----------------------------------------------------------------------
// PlayStandingUp()
// ----------------------------------------------------------------------

function PlayStandingUp()
{
//	ClientMessage("PlayStandingUp()");
    PlayAnimPivot('SitStand', , 0.15);
}


// ----------------------------------------------------------------------
// PlayRubbingEyesStart()
// ----------------------------------------------------------------------

function PlayRubbingEyesStart()
{
//	ClientMessage("PlayRubbingEyesStart()");
    PlayAnimPivot('RubEyesStart', , 0.15);
}


// ----------------------------------------------------------------------
// PlayRubbingEyes()
// ----------------------------------------------------------------------

function PlayRubbingEyes()
{
//	ClientMessage("PlayRubbingEyes()");
    LoopAnimPivot('RubEyes');
}


// ----------------------------------------------------------------------
// PlayRubbingEyesEnd()
// ----------------------------------------------------------------------

function PlayRubbingEyesEnd()
{
//	ClientMessage("PlayRubbingEyesEnd()");
    PlayAnimPivot('RubEyesStop');
}


// ----------------------------------------------------------------------
// PlayCowerBegin()
// ----------------------------------------------------------------------

function PlayCowerBegin()
{
//	ClientMessage("PlayCowerBegin()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',,,,GetSwimPivot());
    else
        PlayAnimPivot('CowerBegin');
}


// ----------------------------------------------------------------------
// PlayCowering()
// ----------------------------------------------------------------------

function PlayCowering()
{
//	ClientMessage("PlayCowering()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',,,,GetSwimPivot());
    else
        LoopAnimPivot('CowerStill');
}


// ----------------------------------------------------------------------
// PlayCowerEnd()
// ----------------------------------------------------------------------

function PlayCowerEnd()
{
//	ClientMessage("PlayCowerEnd()");
    if (Region.Zone.bWaterZone)
        LoopAnimPivot('Tread',,,,GetSwimPivot());
    else
        PlayAnimPivot('CowerEnd');
}


// ----------------------------------------------------------------------
// PlayStunned()
// ----------------------------------------------------------------------

function PlayStunned()
{
//	ClientMessage("PlayStunned()");
    LoopAnimPivot('Shocked');
}


// ----------------------------------------------------------------------
// TweenToSwimming()
// ----------------------------------------------------------------------

function TweenToSwimming(float tweentime)
{
//	ClientMessage("TweenToSwimming()");
    TweenAnimPivot('Tread', tweentime, GetSwimPivot());
}


// ----------------------------------------------------------------------
// PlaySwimming()
// ----------------------------------------------------------------------

function PlaySwimming()
{
//	ClientMessage("PlaySwimming()");
    LoopAnimPivot('Tread', , , , GetSwimPivot());
}


// ----------------------------------------------------------------------
// PlayFalling()
// ----------------------------------------------------------------------

function PlayFalling()
{
//	ClientMessage("PlayFalling()");
    PlayAnimPivot('Jump', 3, 0.1);
}


// ----------------------------------------------------------------------
// PlayLanded()
// ----------------------------------------------------------------------

function PlayLanded(float impactVel)
{
//	ClientMessage("PlayLanded()");
    bIsWalking = True;
    if (impactVel < -12*CollisionHeight)
        PlayAnimPivot('Land');
}


// ----------------------------------------------------------------------
// PlayDuck()
// ----------------------------------------------------------------------

function PlayDuck()
{
//	ClientMessage("PlayDuck()");
    TweenAnimPivot('CrouchWalk', 0.25);
//	PlayAnimPivot('Crouch');
}


// ----------------------------------------------------------------------
// PlayRising()
// ----------------------------------------------------------------------

function PlayRising()
{
//	ClientMessage("PlayRising()");
    PlayAnimPivot('Stand');
}


// ----------------------------------------------------------------------
// PlayCrawling()
// ----------------------------------------------------------------------

function PlayCrawling()
{
//	ClientMessage("PlayCrawling()");
    LoopAnimPivot('CrouchWalk');
}


// ----------------------------------------------------------------------
// PlayPushing()
// ----------------------------------------------------------------------

function PlayPushing()
{
//	ClientMessage("PlayPushing()");
    PlayAnimPivot('PushButton', , 0.15);
}


// ----------------------------------------------------------------------
// PlayBeginAttack()
// ----------------------------------------------------------------------

function bool PlayBeginAttack()
{
    return false;
}


// ----------------------------------------------------------------------
// PlayFiring()
// ----------------------------------------------------------------------

/*
function PlayFiring()
{
    local DeusExWeapon W;

//  ClientMessage("PlayFiring()");

    W = DeusExWeapon(Weapon);

    if (W != None)
    {
        if (W.bHandToHand)
        {
            PlayAnimPivot('Attack',,0.1);
        }
        else
        {
            if (W.bAutomatic)
            {
                if (HasTwoHandedWeapon())
                    LoopAnimPivot('Shoot2H',,0.1);
                else
                    LoopAnimPivot('Shoot',,0.1);
            }
            else
            {
                if (HasTwoHandedWeapon())
                    PlayAnimPivot('Shoot2H',,0.1);
                else
                    PlayAnimPivot('Shoot',,0.1);
            }
        }
    }
}
*/


// ----------------------------------------------------------------------
// PlayTakingHit()
// ----------------------------------------------------------------------

function PlayTakingHit(EHitLocation hitPos)
{
    local vector pivot;
    local name   animName;

    animName = '';
    if (!Region.Zone.bWaterZone)
    {
        switch (hitPos)
        {
            case HITLOC_HeadFront:
                animName = 'HitHead';
                break;
            case HITLOC_TorsoFront:
                animName = 'HitTorso';
                break;
            case HITLOC_LeftArmFront:
                animName = 'HitArmLeft';
                break;
            case HITLOC_RightArmFront:
                animName = 'HitArmRight';
                break;

            case HITLOC_HeadBack:
                animName = 'HitHeadBack';
                break;
            case HITLOC_TorsoBack:
            case HITLOC_LeftArmBack:
            case HITLOC_RightArmBack:
                animName = 'HitTorsoBack';
                break;

            case HITLOC_LeftLegFront:
            case HITLOC_LeftLegBack:
                animName = 'HitLegLeft';
                break;

            case HITLOC_RightLegFront:
            case HITLOC_RightLegBack:
                animName = 'HitLegRight';
                break;
        }
        pivot = vect(0,0,0);
    }
    else
    {
        switch (hitPos)
        {
            case HITLOC_HeadFront:
            case HITLOC_TorsoFront:
            case HITLOC_LeftLegFront:
            case HITLOC_RightLegFront:
            case HITLOC_LeftArmFront:
            case HITLOC_RightArmFront:
                animName = 'WaterHitTorso';
                break;

            case HITLOC_HeadBack:
            case HITLOC_TorsoBack:
            case HITLOC_LeftLegBack:
            case HITLOC_RightLegBack:
            case HITLOC_LeftArmBack:
            case HITLOC_RightArmBack:
                animName = 'WaterHitTorsoBack';
                break;
        }
        pivot = GetSwimPivot();
    }

    if (animName != '')
        PlayAnimPivot(animName, , 0.1, pivot);

}


// ----------------------------------------------------------------------
// PlayWeaponSwitch()
// ----------------------------------------------------------------------

function PlayWeaponSwitch(Weapon newWeapon)
{
//	ClientMessage("PlayWeaponSwitch()");
}


// ----------------------------------------------------------------------
// PlayDying()
// ----------------------------------------------------------------------

function PlayDying(name damageType, vector hitLoc)
{
    local Vector X, Y, Z;
    local float dotp;

//	ClientMessage("PlayDying()");
    if (Region.Zone.bWaterZone)
        PlayAnimPivot('WaterDeath',, 0.1);
    else if (bSitting)  // if sitting, always fall forward
        PlayAnimPivot('DeathFront',, 0.1);
    else
    {
        GetAxes(Rotation, X, Y, Z);
        dotp = (Location - HitLoc) dot X;

        // die from the correct side
        if (dotp < 0.0)     // shot from the front, fall back
            PlayAnimPivot('DeathBack',, 0.1);
        else                // shot from the back, fall front
            PlayAnimPivot('DeathFront',, 0.1);
    }

    // don't scream if we are stunned
    if ((damageType == 'Stunned') || (damageType == 'KnockedOut') ||
        (damageType == 'Poison') || (damageType == 'PoisonEffect'))
    {
        bStunned = True;
        if (bIsFemale)
            PlaySound(Sound'FemaleUnconscious', SLOT_Pain,,,, RandomPitch());
        else
            PlaySound(Sound'MaleUnconscious', SLOT_Pain,,,, RandomPitch());
    }
    else
    {
        bStunned = False;
        PlayDyingSound();
    }
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// FIRE ROUTINES
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// CatchFire()
// ----------------------------------------------------------------------

function CatchFire()
{
    local Fire f;
    local int i;
    local vector loc;

    if (bOnFire || Region.Zone.bWaterZone || (BurnPeriod <= 0) || bInvincible)
        return;

    bOnFire = True;
    burnTimer = 0;

    EnableCloak(false);

    for (i=0; i<8; i++)
    {
        loc.X = 0.5*CollisionRadius * (1.0-2.0*FRand());
        loc.Y = 0.5*CollisionRadius * (1.0-2.0*FRand());
        loc.Z = 0.6*CollisionHeight * (1.0-2.0*FRand());
        loc += Location;
        f = Spawn(class'Fire', Self,, loc);
        if (f != None)
        {
            f.DrawScale = 0.5*FRand() + 1.0;

            // turn off the sound and lights for all but the first one
            if (i > 0)
            {
                f.AmbientSound = None;
                f.LightType = LT_None;
            }

            // turn on/off extra fire and smoke
            if (FRand() < 0.5)
                f.smokeGen.Destroy();
            if (FRand() < 0.5)
                f.AddFire();
        }
    }

    // set the burn timer
    SetTimer(1.0, True);
}

// ----------------------------------------------------------------------
// ExtinguishFire()
// ----------------------------------------------------------------------

function ExtinguishFire()
{
    local Fire f;

    bOnFire = False;
    burnTimer = 0;
    SetTimer(0, False);

    foreach BasedActors(class'Fire', f)
        f.Destroy();
}

// ----------------------------------------------------------------------
// UpdateFire()
// ----------------------------------------------------------------------

function UpdateFire()
{
    // continually burn and do damage
    HealthTorso -= 5;
    GenerateTotalHealth();
    if (Health <= 0)
    {
        TakeDamage(10, None, Location, vect(0,0,0), 'Burned');
        ExtinguishFire();
    }
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// CONVERSATION FUNCTIONS
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// CanConverse()
// ----------------------------------------------------------------------

function bool CanConverse()
{
    // Return True if this NPC is in a conversable state
    return (bCanConverse && bInterruptState && ((Physics == PHYS_Walking) || (Physics == PHYS_Flying)));
}


// ----------------------------------------------------------------------
// CanConverseWithPlayer()
// ----------------------------------------------------------------------

function bool CanConverseWithPlayer(DeusExPlayer dxPlayer)
{
    local name alliance1, alliance2, carcname;  // temp vars

    if (GetPawnAllianceType(dxPlayer) == ALLIANCE_Hostile)
        return false;
    else if ((GetStateName() == 'Fleeing') && (Enemy != dxPlayer) && (IsValidEnemy(Enemy, false)))  // hack
        return false;
    else if (GetCarcassData(dxPlayer, alliance1, alliance2, carcname))
        return false;
    else
        return true;
}


// ----------------------------------------------------------------------
// EndConversation()
// ----------------------------------------------------------------------

function EndConversation()
{
    Super.EndConversation();

    if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation'))
    {
        bConversationEndedNormally = True;

        if (!bConvEndState)
            FollowOrders();
    }

    bInConversation = False;
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// STIMULI AND AGITATION ROUTINES
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// LoudNoiseScore()
// ----------------------------------------------------------------------

function float LoudNoiseScore(actor receiver, actor sender, float score)
{
    local Pawn pawnSender;

    // Cull events received from friends
    pawnSender = Pawn(sender);
    if (pawnSender == None)
        pawnSender = sender.Instigator;
    if (pawnSender == None)
        score = 0;
    else if (!IsValidEnemy(pawnSender))
        score = 0;

    return score;
}


// ----------------------------------------------------------------------
// WeaponDrawnScore()
// ----------------------------------------------------------------------

function float WeaponDrawnScore(actor receiver, actor sender, float score)
{
    local Pawn pawnSender;

    // Cull events received from enemies
    pawnSender = Pawn(sender);
    if (pawnSender == None)
        pawnSender = Pawn(sender.Owner);
    if (pawnSender == None)
        pawnSender = sender.Instigator;
    if (pawnSender == None)
        score = 0;
    else if (IsValidEnemy(pawnSender))
        score = 0;

    return score;
}


// ----------------------------------------------------------------------
// DistressScore()
// ----------------------------------------------------------------------

function float DistressScore(actor receiver, actor sender, float score)
{
    local ScriptedPawn scriptedSender;
    local Pawn         pawnSender;

    // Cull events received from enemies
    sender         = InstigatorToPawn(sender);  // hack
    pawnSender     = Pawn(sender);
    scriptedSender = ScriptedPawn(sender);
    if (pawnSender == None)
        score = 0;
    else if ((GetPawnAllianceType(pawnSender) != ALLIANCE_Friendly) && !bFearDistress)
        score = 0;
    else if ((scriptedSender != None) && !scriptedSender.bDistressed)
        score = 0;

    return score;
}


// ----------------------------------------------------------------------
// UpdateReactionCallbacks()
// ----------------------------------------------------------------------

function UpdateReactionCallbacks()
{
    if (bReactFutz && bLookingForFutz)
        AISetEventCallback('Futz', 'HandleFutz', , true, true, false, true);
    else
        AIClearEventCallback('Futz');

    if ((bHateHacking || bFearHacking) && bLookingForHacking)
        AISetEventCallback('MegaFutz', 'HandleHacking', , true, true, true, true);
    else
        AIClearEventCallback('MegaFutz');

    if ((bHateWeapon || bFearWeapon) && bLookingForWeapon)
        AISetEventCallback('WeaponDrawn', 'HandleWeapon', 'WeaponDrawnScore', true, true, false, true);
    else
        AIClearEventCallback('WeaponDrawn');

    if ((bReactShot || bFearShot || bHateShot) && bLookingForShot)
        AISetEventCallback('WeaponFire', 'HandleShot', , true, true, false, true);
    else
        AIClearEventCallback('WeaponFire');

/*
    if ((bHateCarcass || bReactCarcass || bFearCarcass) && bLookingForCarcass)
        AISetEventCallback('Carcass', 'HandleCarcass', 'CarcassScore', true, true, false, true);
    else
        AIClearEventCallback('Carcass');
*/

    if (bReactLoudNoise && bLookingForLoudNoise)
        AISetEventCallback('LoudNoise', 'HandleLoudNoise', 'LoudNoiseScore');
    else
        AIClearEventCallback('LoudNoise');

    if ((bReactAlarm || bFearAlarm) && bLookingForAlarm)
        AISetEventCallback('Alarm', 'HandleAlarm');
    else
        AIClearEventCallback('Alarm');

    if ((bHateDistress || bReactDistress || bFearDistress) && bLookingForDistress)
        AISetEventCallback('Distress', 'HandleDistress', 'DistressScore', true, true, false, true);
    else
        AIClearEventCallback('Distress');

    if ((bFearProjectiles || bReactProjectiles) && bLookingForProjectiles)
        AISetEventCallback('Projectile', 'HandleProjectiles', , false, true, false, true);
    else
        AIClearEventCallback('Projectile');
}


// ----------------------------------------------------------------------
// SetReactions()
// ----------------------------------------------------------------------

function SetReactions(bool bEnemy, bool bLoudNoise, bool bAlarm, bool bDistress,
                      bool bProjectile, bool bFutz, bool bHacking, bool bShot, bool bWeapon, bool bCarcass,
                      bool bInjury, bool bIndirectInjury)
{
    bLookingForEnemy          = bEnemy;
    bLookingForLoudNoise      = bLoudNoise;
    bLookingForAlarm          = bAlarm;
    bLookingForDistress       = bDistress;
    bLookingForProjectiles    = bProjectile;
    bLookingForFutz           = bFutz;
    bLookingForHacking        = bHacking;
    bLookingForShot           = bShot;
    bLookingForWeapon         = bWeapon;
    bLookingForCarcass        = bCarcass;
    bLookingForInjury         = bInjury;
    bLookingForIndirectInjury = bIndirectInjury;

    UpdateReactionCallbacks();

}


// ----------------------------------------------------------------------
// BlockReactions()
// ----------------------------------------------------------------------

function BlockReactions(optional bool bBlockInjury)
{
    SetReactions(false, false, false, false, false, false, false, false, false, false, !bBlockInjury, !bBlockInjury);
}


// ----------------------------------------------------------------------
// ResetReactions()
// ----------------------------------------------------------------------

function ResetReactions()
{
    SetReactions(true, true, true, true, true, true, true, true, true, true, true, true);
}


// ----------------------------------------------------------------------
// HandleFutz()
// ----------------------------------------------------------------------

function HandleFutz(Name event, EAIEventState state, XAIParams params)
{
    // React

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
        ReactToFutz();  // only players can futz
}


// ----------------------------------------------------------------------
// HandleHacking()
// ----------------------------------------------------------------------

function HandleHacking(Name event, EAIEventState state, XAIParams params)
{
    // Fear, Hate

    local Pawn pawnActor;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        pawnActor = GetPlayerPawn();
        if (pawnActor != None)
        {
            if (bHateHacking)
                IncreaseAgitation(pawnActor, 1.0);
            if (bFearHacking)
                IncreaseFear(pawnActor, 0.51);
            if (SetEnemy(pawnActor))
            {
                SetDistressTimer();
                HandleEnemy();
            }
            else if (bFearHacking && IsFearful())
            {
                SetDistressTimer();
                SetEnemy(pawnActor, , true);
                GotoState('Fleeing');
            }
            else  // only players can hack
                ReactToFutz();
        }
    }
}


// ----------------------------------------------------------------------
// HandleWeapon()
// ----------------------------------------------------------------------

function HandleWeapon(Name event, EAIEventState state, XAIParams params)
{
    // Fear, Hate

    local Pawn pawnActor;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        pawnActor = InstigatorToPawn(params.bestActor);
        if (pawnActor != None)
        {
            if (bHateWeapon)
                IncreaseAgitation(pawnActor);
            if (bFearWeapon)
                IncreaseFear(pawnActor, 1.0);

            // Let presence checking handle enemy sighting

            if (!IsValidEnemy(pawnActor))
            {
                if (bFearWeapon && IsFearful())
                {
                    SetDistressTimer();
                    SetEnemy(pawnActor, , true);
                    GotoState('Fleeing');
                }
                else if (pawnActor.bIsPlayer)
                    ReactToFutz();
            }
        }
    }
}


// ----------------------------------------------------------------------
// HandleShot()
// ----------------------------------------------------------------------

function HandleShot(Name event, EAIEventState state, XAIParams params)
{
    // React, Fear, Hate

    local Pawn pawnActor;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        pawnActor = InstigatorToPawn(params.bestActor);
        if (pawnActor != None)
        {
            if (bHateShot)
                IncreaseAgitation(pawnActor);
            if (bFearShot)
                IncreaseFear(pawnActor, 1.0);
            if (SetEnemy(pawnActor))
            {
                SetDistressTimer();
                HandleEnemy();
            }
            else if (bFearShot && IsFearful())
            {
                SetDistressTimer();
                SetEnemy(pawnActor, , true);
                GotoState('Fleeing');
            }
            else if (pawnActor.bIsPlayer)
                ReactToFutz();
        }
    }
}


// ----------------------------------------------------------------------
// HandleLoudNoise()
// ----------------------------------------------------------------------

function HandleLoudNoise(Name event, EAIEventState state, XAIParams params)
{
    // React

    local Actor bestActor;
    local Pawn  instigator;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        bestActor = params.bestActor;
        if (bestActor != None)
        {
            instigator = Pawn(bestActor);
            if (instigator == None)
                instigator = bestActor.Instigator;
            if (instigator != None)
            {
                if (IsValidEnemy(instigator))
                {
                    SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound);
                    HandleEnemy();
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// HandleProjectiles()
// ----------------------------------------------------------------------

function HandleProjectiles(Name event, EAIEventState state, XAIParams params)
{
    // React, Fear

    local DeusExProjectile dxProjectile;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
        if (params.bestActor != None)
            ReactToProjectiles(params.bestActor);
}


// ----------------------------------------------------------------------
// HandleAlarm()
// ----------------------------------------------------------------------

function HandleAlarm(Name event, EAIEventState state, XAIParams params)
{
    // React, Fear

    local AlarmUnit      alarm;
    local LaserTrigger   laser;
    local SecurityCamera camera;
    local Computers      computer;
    local Pawn           alarmInstigator;
    local vector         alarmLocation;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        alarmInstigator = None;
        alarm    = AlarmUnit(params.bestActor);
        laser    = LaserTrigger(params.bestActor);
        camera   = SecurityCamera(params.bestActor);
        computer = Computers(params.bestActor);
        if (alarm != None)
        {
            alarmInstigator = alarm.alarmInstigator;
            alarmLocation   = alarm.alarmLocation;
        }
        else if (laser != None)
        {
            alarmInstigator = Pawn(laser.triggerActor);
            if (alarmInstigator == None)
                alarmInstigator = laser.triggerActor.Instigator;
            alarmLocation   = laser.actorLocation;
        }
        else if (camera != None)
        {
            alarmInstigator = GetPlayerPawn();  // player is implicit for cameras
            alarmLocation   = camera.playerLocation;
        }
        else if (computer != None)
        {
            alarmInstigator = GetPlayerPawn();  // player is implicit for computers
            alarmLocation   = computer.Location;
        }

        if (bFearAlarm)
        {
            IncreaseFear(alarmInstigator, 2.0);
            if (IsFearful())
            {
                SetDistressTimer();
                SetEnemy(alarmInstigator, , true);
                GotoState('Fleeing');
            }
        }

        if (alarmInstigator != None)
        {
            if (alarmInstigator.Health > 0)
            {
                if (IsValidEnemy(alarmInstigator))
                {
                    AlarmTimer = 120;
                    SetDistressTimer();
                    SetSeekLocation(alarmInstigator, alarmLocation, SEEKTYPE_Sound);
                    HandleEnemy();
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// HandleDistress()
// ----------------------------------------------------------------------

function HandleDistress(Name event, EAIEventState state, XAIParams params)
{
    // React, Fear, Hate

    local float        seeTime;
    local Pawn         distressee;
    local DeusExPlayer distresseePlayer;
    local ScriptedPawn distresseePawn;
    local Pawn         distressor;
    local DeusExPlayer distressorPlayer;
    local ScriptedPawn distressorPawn;
    local bool         bDistresseeValid;
    local bool         bDistressorValid;
    local float        distressVal;
    local name         stateName;
    local bool         bAttacking;
    local bool         bFleeing;

    bAttacking = false;
    seeTime    = 0;

    if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
    {
        distressee = InstigatorToPawn(params.bestActor);
        if (distressee != None)
        {
            if (bFearDistress)
                IncreaseFear(distressee.Enemy, 1.0);
            bDistresseeValid = false;
            bDistressorValid = false;
            distresseePlayer = DeusExPlayer(distressee);
            distresseePawn   = ScriptedPawn(distressee);
            if (GetPawnAllianceType(distressee) == ALLIANCE_Friendly)
            {
                if (distresseePawn != None)
                {
                    if (distresseePawn.bDistressed && (distresseePawn.EnemyLastSeen <= EnemyTimeout))
                    {
                        bDistresseeValid = true;
                        seeTime          = distresseePawn.EnemyLastSeen;
                    }
                }
                else if (distresseePlayer != None)
                    bDistresseeValid = true;
            }
            if (bDistresseeValid)
            {
                distressor       = distressee.Enemy;
                distressorPlayer = DeusExPlayer(distressor);
                distressorPawn   = ScriptedPawn(distressor);
                if (distressorPawn != None)
                {
                    if (bHateDistress || (distressorPawn.GetPawnAllianceType(distressee) == ALLIANCE_Hostile))
                        bDistressorValid = true;
                }
                else if (distresseePawn != None)
                {
                    if (bHateDistress || (distresseePawn.GetPawnAllianceType(distressor) == ALLIANCE_Hostile))
                        bDistressorValid = true;
                }

                // Finally, react
                if (bDistressorValid)
                {
                    if (bHateDistress)
                        IncreaseAgitation(distressor, 1.0);
                    if (SetEnemy(distressor, seeTime))
                    {
                        SetDistressTimer();
                        HandleEnemy();
                        bAttacking = true;
                    }
                }
                // BOOGER! Make NPCs react by seeking if distressor isn't an enemy?
            }

            if (!bAttacking && bFearDistress)
            {
                distressVal = 0;
                bFleeing    = false;
                if (distresseePawn != None)
                {
                    stateName = distresseePawn.GetStateName();
                    if (stateName == 'Fleeing')  // hack -- to prevent infinite fleeing
                    {
                        if (distresseePawn.DistressTimer >= 0)
                        {
                            if (FearSustainTime - distresseePawn.DistressTimer >= 1)
                            {
                                IncreaseFear(distressee.Enemy, 1.0, distresseePawn.DistressTimer);
                                distressVal = distresseePawn.DistressTimer;
                                bFleeing    = true;
                            }
                        }
                    }
                    else
                    {
                        IncreaseFear(distressee.Enemy, 1.0);
                        bFleeing = true;
                    }
                }
                else
                {
                    IncreaseFear(distressee.Enemy, 1.0);
                    bFleeing = true;
                }
                if (bFleeing && IsFearful())
                {
                    if ((DistressTimer > distressVal) || (DistressTimer < 0))
                        DistressTimer = distressVal;
                    SetEnemy(distressee.Enemy, , true);
                    GotoState('Fleeing');
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// IncreaseFear()
// ----------------------------------------------------------------------

function IncreaseFear(Actor actorInstigator, float addedFearLevel,
                      optional float newFearTimer)
{
    local DeusExPlayer player;
    local Pawn         instigator;

    instigator = InstigatorToPawn(actorInstigator);
    if (instigator != None)
    {
        if (FearTimer < (FearSustainTime-newFearTimer))
            FearTimer = FearSustainTime-newFearTimer;
        if (FearTimer > 0)
        {
            if (addedFearLevel > 0)
            {
                FearLevel += addedFearLevel;
                if (FearLevel > 1.0)
                    FearLevel = 1.0;
            }
        }
    }
}


// ----------------------------------------------------------------------
// IncreaseAgitation()
// ----------------------------------------------------------------------

function IncreaseAgitation(Actor actorInstigator, optional float AgitationLevel)
{
    local Pawn  instigator;
    local float minLevel;

    instigator = InstigatorToPawn(actorInstigator);
    if (instigator != None)
    {
        AgitationTimer = AgitationSustainTime;
        if (AgitationCheckTimer <= 0)
        {
            AgitationCheckTimer = 1.5;  // hardcoded for now
            if (AgitationLevel == 0)
            {
                if (MaxProvocations < 0)
                    MaxProvocations = 0;
                AgitationLevel = 1.0/(MaxProvocations+1);
            }
            if (AgitationLevel > 0)
            {
                bAlliancesChanged    = True;
                bNoNegativeAlliances = False;
                AgitateAlliance(instigator.Alliance, AgitationLevel);
            }
        }
    }

}


// ----------------------------------------------------------------------
// DecreaseAgitation()
// ----------------------------------------------------------------------

function DecreaseAgitation(Actor actorInstigator, float AgitationLevel)
{
    local float        newLevel;
    local DeusExPlayer player;
    local Pawn         instigator;

    player = DeusExPlayer(GetPlayerPawn());

    if (Inventory(actorInstigator) != None)
    {
        if (Inventory(actorInstigator).Owner != None)
            actorInstigator = Inventory(actorInstigator).Owner;
    }
    else if (DeusExDecoration(actorInstigator) != None)
        actorInstigator = player;

    instigator = Pawn(actorInstigator);
    if ((instigator == None) || (instigator == self))
        return;

    AgitationTimer  = AgitationSustainTime;
    if (AgitationLevel > 0)
    {
        bAlliancesChanged    = True;
        bNoNegativeAlliances = False;
        AgitateAlliance(instigator.Alliance, -AgitationLevel);
    }

}


// ----------------------------------------------------------------------
// UpdateAgitation()
// ----------------------------------------------------------------------

function UpdateAgitation(float deltaSeconds)
{
    local float mult;
    local float decrement;
    local int   i;

    if (AgitationCheckTimer > 0)
    {
        AgitationCheckTimer -= deltaSeconds;
        if (AgitationCheckTimer < 0)
            AgitationCheckTimer = 0;
    }

    decrement = 0;
    if (AgitationTimer > 0)
    {
        if (AgitationTimer < deltaSeconds)
        {
            mult = 1.0 - (AgitationTimer/deltaSeconds);
            AgitationTimer = 0;
            decrement = mult * (AgitationDecayRate*deltaSeconds);
        }
        else
            AgitationTimer -= deltaSeconds;
    }
    else
        decrement = AgitationDecayRate*deltaSeconds;

    if (bAlliancesChanged && (decrement > 0))
    {
        bAlliancesChanged = False;
        for (i=15; i>=0; i--)
        {
            if ((AlliancesEx[i].AllianceName != '') && (!AlliancesEx[i].bPermanent))
            {
                if (AlliancesEx[i].AgitationLevel > 0)
                {
                    bAlliancesChanged = true;
                    AlliancesEx[i].AgitationLevel -= decrement;
                    if (AlliancesEx[i].AgitationLevel < 0)
                        AlliancesEx[i].AgitationLevel = 0;
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// UpdateFear()
// ----------------------------------------------------------------------

function UpdateFear(float deltaSeconds)
{
    local float mult;
    local float decrement;
    local int   i;

    decrement = 0;
    if (FearTimer > 0)
    {
        if (FearTimer < deltaSeconds)
        {
            mult = 1.0 - (FearTimer/deltaSeconds);
            FearTimer = 0;
            decrement = mult * (FearDecayRate*deltaSeconds);
        }
        else
            FearTimer -= deltaSeconds;
    }
    else
        decrement = FearDecayRate*deltaSeconds;

    if ((decrement > 0) && (FearLevel > 0))
    {
        FearLevel -= decrement;
        if (FearLevel < 0)
            FearLevel = 0;
    }
}


// ----------------------------------------------------------------------
// IsFearful()
// ----------------------------------------------------------------------

function bool IsFearful()
{
    if (FearLevel >= 1.0)
        return true;
    else
        return false;
}


// ----------------------------------------------------------------------
// ShouldBeStartled()  [stub function, overridden by subclasses]
// ----------------------------------------------------------------------

function bool ShouldBeStartled(Pawn startler)
{
    return false;
}


// ----------------------------------------------------------------------
// ShouldPlayTurn()
// ----------------------------------------------------------------------

function bool ShouldPlayTurn(vector lookdir)
{
    local Rotator rot;

    rot = Rotator(lookdir);
    rot.Yaw = (rot.Yaw - Rotation.Yaw) & 65535;
    if (rot.Yaw > 32767)
        rot.Yaw = 65536 - rot.Yaw;  // negate
    if (rot.Yaw > 4096)
        return true;
    else
        return false;
}


// ----------------------------------------------------------------------
// ShouldPlayWalk()
// ----------------------------------------------------------------------

function bool ShouldPlayWalk(vector movedir)
{
    local vector diff;

    if (Physics == PHYS_Falling)
        return true;
    else if (Physics == PHYS_Walking)
    {
        diff = (movedir - Location) * vect(1,1,0);
        if (VSize(diff) < 16)
            return false;
        else
            return true;
    }
    else if (VSize(movedir-Location) < 16)
        return false;
    else
        return true;
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ALLIANCE ROUTINES
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// SetAlliance()
// ----------------------------------------------------------------------

function SetAlliance(Name newAlliance)
{
    Alliance = newAlliance;
}


// ----------------------------------------------------------------------
// ChangeAlly()
// ----------------------------------------------------------------------

function ChangeAlly(Name newAlly, optional float allyLevel, optional bool bPermanent, optional bool bHonorPermanence)
{
    local int i;

    // Members of the same alliance will ALWAYS be friendly to each other
    if (newAlly == Alliance)
    {
        allyLevel  = 1;
        bPermanent = true;
    }

    if (bHonorPermanence)
    {
        for (i=0; i<16; i++)
            if (AlliancesEx[i].AllianceName == newAlly)
                if (AlliancesEx[i].bPermanent)
                    break;
        if (i < 16)
            return;
    }

    if (allyLevel < -1)
        allyLevel = -1;
    if (allyLevel > 1)
        allyLevel = 1;

    for (i=0; i<16; i++)
        if ((AlliancesEx[i].AllianceName == newAlly) || (AlliancesEx[i].AllianceName == ''))
            break;

    if (i >= 16)
        for (i=15; i>0; i--)
            AlliancesEx[i] = AlliancesEx[i-1];

    AlliancesEx[i].AllianceName         = newAlly;
    AlliancesEx[i].AllianceLevel        = allyLevel;
    AlliancesEx[i].AgitationLevel       = 0;
    AlliancesEx[i].bPermanent           = bPermanent;

    bAlliancesChanged    = True;
    bNoNegativeAlliances = False;
}


// ----------------------------------------------------------------------
// AgitateAlliance()
// ----------------------------------------------------------------------

function AgitateAlliance(Name newEnemy, float agitation)
{
    local int   i;
    local float oldLevel;
    local float newLevel;

    if (newEnemy != '')
    {
        for (i=0; i<16; i++)
            if ((AlliancesEx[i].AllianceName == newEnemy) || (AlliancesEx[i].AllianceName == ''))
                break;

        if (i < 16)
        {
            if ((AlliancesEx[i].AllianceName == '') || !(AlliancesEx[i].bPermanent))
            {
                if (AlliancesEx[i].AllianceName == '')
                    AlliancesEx[i].AllianceLevel = 0;
                oldLevel = AlliancesEx[i].AgitationLevel;
                newLevel = oldLevel + agitation;
                if (newLevel > 1.0)
                    newLevel = 1.0;
                AlliancesEx[i].AllianceName   = newEnemy;
                AlliancesEx[i].AgitationLevel = newLevel;
                if ((newEnemy == 'Player') && (oldLevel < 1.0) && (newLevel >= 1.0))  // hack
                    PlayerAgitationTimer = 2.0;
                bAlliancesChanged = True;
            }
        }
    }
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ATTACKING FUNCTIONS
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// AISafeToShoot()
// ----------------------------------------------------------------------

function bool AISafeToShoot(out Actor hitActor, vector traceEnd, vector traceStart,
                            optional vector extent, optional bool bIgnoreLevel)
{
    local Actor            traceActor;
    local Vector           hitLocation;
    local Vector           hitNormal;
    local Pawn             tracePawn;
    local DeusExDecoration traceDecoration;
    local DeusExMover      traceMover;
    local bool             bSafe;

    // Future improvement:
    // Ideally, this should use the ammo type to determine how many shots
    // it will take to destroy an obstruction, and call it unsafe if it takes
    // more than x shots.  Also, if the ammo is explosive, and the
    // obstruction is too close, it should never be safe...

    bSafe    = true;
    hitActor = None;

    foreach TraceActors(Class'Actor', traceActor, hitLocation, hitNormal,
                        traceEnd, traceStart, extent)
    {
        if (hitActor == None)
            hitActor = traceActor;
        if (traceActor == Level)
        {
            if (!bIgnoreLevel)
                bSafe = false;
            break;
        }
        tracePawn = Pawn(traceActor);
        if (tracePawn != None)
        {
            if (tracePawn != self)
            {
                if (GetPawnAllianceType(tracePawn) == ALLIANCE_Friendly)
                    bSafe = false;
                break;
            }
        }
        traceDecoration = DeusExDecoration(traceActor);
        if (traceDecoration != None)
        {
            if (traceDecoration.bExplosive || traceDecoration.bInvincible)
            {
                bSafe = false;
                break;
            }
            if ((traceDecoration.HitPoints > 20) || (traceDecoration.minDamageThreshold > 4))  // hack
            {
                bSafe = false;
                break;
            }
        }
        traceMover = DeusExMover(traceActor);
        if (traceMover != None)
        {
            if (!traceMover.bBreakable)
            {
                bSafe = false;
                break;
            }
            else if ((traceMover.doorStrength > 0.2) || (traceMover.minDamageThreshold > 8))  // hack
            {
                bSafe = false;
                break;
            }
            else  // hack
                break;
        }
        if (Inventory(traceActor) != None)
        {
            bSafe = false;
            break;
        }
    }

    return (bSafe);
}


// ----------------------------------------------------------------------
// ComputeThrowAngles()
// ----------------------------------------------------------------------

function bool ComputeThrowAngles(vector traceEnd, vector traceStart,
                                 float speed,
                                 out Rotator angle1, out Rotator angle2)
{
    local float   deltaX, deltaY;
    local float   x, y;
    local float   tanAngle1, tanAngle2;
    local float   A, B, C;
    local float   m, n;
    local float   sqrtTerm;
    local float   gravity;
    local float   traceYaw;
    local bool    bValid;

    bValid = false;

    // Reduce our problem to two dimensions
    deltaX = traceEnd.X - traceStart.X;
    deltaY = traceEnd.Y - traceStart.Y;
    x = sqrt(deltaX*deltaX + deltaY*deltaY);
    y = traceEnd.Z - traceStart.Z;

    gravity = -Region.Zone.ZoneGravity.Z;
    if ((x > 0) && (gravity > 0))
    {
        A = -gravity*x*x;
        B = 2*speed*speed*x;
        C = -gravity*x*x - 2*y*speed*speed;

        sqrtTerm = B*B - 4*A*C;
        if (sqrtTerm >= 0)
        {
            m = -B/(2*A);
            n = sqrt(sqrtTerm)/(2*A);

            tanAngle1 = atan(m+n);
            tanAngle2 = atan(m-n);

            angle1 = Rotator(traceEnd - traceStart);
            angle2 = angle1;
            angle1.Pitch = tanAngle1*32768/Pi;
            angle2.Pitch = tanAngle2*32768/Pi;

            bValid = true;
        }
    }

    return bValid;
}


// ----------------------------------------------------------------------
// AISafeToThrow()
// ----------------------------------------------------------------------

function bool AISafeToThrow(vector traceEnd, vector traceStart,
                            float throwAccuracy,
                            optional vector extent)
{
    local float                   time1, time2, tempTime;
    local vector                  pos1,  pos2,  tempPos;
    local rotator                 rot1,  rot2,  tempRot;
    local rotator                 bestAngle;
    local bool                    bSafe;
    local DeusExWeapon            dxWeapon;
    local Class<ThrownProjectile> throwClass;

    // Someday, we should check for nearby friendlies within the blast radius
    // before throwing...

    // Sanity checks
    throwClass = None;
    dxWeapon = DeusExWeapon(Weapon);
    if (dxWeapon != None)
        throwClass = Class<ThrownProjectile>(dxWeapon.ProjectileClass);
    if (throwClass == None)
        return false;

    if (extent == vect(0,0,0))
    {
        extent = vect(1,1,0) * throwClass.Default.CollisionRadius;
        extent.Z = throwClass.Default.CollisionHeight;
    }

    if (throwAccuracy < 0.01)
        throwAccuracy = 0.01;

    bSafe = false;
    if (ComputeThrowAngles(traceEnd, traceStart, dxWeapon.ProjectileSpeed, rot1, rot2))
    {
        time1 = ParabolicTrace(pos1, Vector(rot1)*dxWeapon.ProjectileSpeed, traceStart,
                               true, extent, 5.0,
                               throwClass.Default.Elasticity, throwClass.Default.bBounce,
                               60, throwAccuracy);
        time2 = ParabolicTrace(pos2, Vector(rot2)*dxWeapon.ProjectileSpeed, traceStart,
                               true, extent, 5.0,
                               throwClass.Default.Elasticity, throwClass.Default.bBounce,
                               60, throwAccuracy);
        if ((time1 > 0) || (time2 > 0))
        {
            if ((time1 > time2) && (time2 > 0))
            {
                tempTime = time1;
                time1    = time2;
                time2    = tempTime;
                tempPos  = pos1;
                pos1     = pos2;
                pos2     = tempPos;
                tempRot  = rot1;
                rot1     = rot2;
                rot2     = tempRot;
            }
            if (VSize(pos1-traceEnd) <= throwClass.Default.blastRadius)
            {
                if (FastTrace(traceEnd, pos1))
                {
                    if ((VSize(pos1-Location) > throwClass.Default.blastRadius*0.5) ||
                        !FastTrace(Location, pos1))
                    {
                        bestAngle = rot1;
                        bSafe     = true;
                    }
                }
            }
        }
        if (!bSafe && (time2 > 0))
        {
            if (VSize(pos2-traceEnd) <= throwClass.Default.blastRadius)
            {
                if (FastTrace(traceEnd, pos2))
                {
                    if ((VSize(pos2-Location) > throwClass.Default.blastRadius*0.5) ||
                        !FastTrace(Location, pos2))
                    {
                        bestAngle = rot2;
                        bSafe     = true;
                    }
                }
            }
        }

    }

    if (bSafe)
        ViewRotation = bestAngle;

    return (bSafe);

}


// ----------------------------------------------------------------------
// AICanShoot()
// ----------------------------------------------------------------------

function bool AICanShoot(pawn target, bool bLeadTarget, bool bCheckReadiness,
                         optional float throwAccuracy, optional bool bDiscountMinRange)
{
    local DeusExWeapon dxWeapon;
    local Vector X, Y, Z;
    local Vector projStart, projEnd;
    local float  tempMinRange, tempMaxRange;
    local float  temp;
    local float  dist;
    local float  extraDist;
    local actor  hitActor;
    local Vector hitLocation, hitNormal;
    local Vector extent;
    local bool   bIsThrown;
    local float  elevation;
    local bool   bSafe;

    if (target == None)
        return false;
    if (target.bIgnore)
        return false;

    dxWeapon = DeusExWeapon(Weapon);
    if (dxWeapon == None)
        return false;
    if (bCheckReadiness && !dxWeapon.bReadyToFire)
        return false;

    if (dxWeapon.ReloadCount > 0)
    {
        if (dxWeapon.AmmoType == None)
            return false;
        if (dxWeapon.AmmoType.AmmoAmount <= 0)
            return false;
    }
    if (FireElevation > 0)
    {
        elevation = FireElevation + (CollisionHeight+target.CollisionHeight);
        if (elevation < 10)
            elevation = 10;
        if (Abs(Location.Z-target.Location.Z) > elevation)
            return false;
    }
    bIsThrown = IsThrownWeapon(dxWeapon);

    extraDist = target.CollisionRadius;
    //extraDist = 0;

    GetPawnWeaponRanges(self, tempMinRange, tempMaxRange, temp);

    if (bDiscountMinRange)
        tempMinRange = 0;

    if (tempMinRange >= tempMaxRange)
        return false;

    ViewRotation = Rotation;
    GetAxes(ViewRotation, X, Y, Z);
    projStart = dxWeapon.ComputeProjectileStart(X, Y, Z);
    if (bLeadTarget && !dxWeapon.bInstantHit && (dxWeapon.ProjectileSpeed > 0))
    {
        if (bIsThrown)
        {
            // compute target's position 1.5 seconds in the future
            projEnd = target.Location + (target.Velocity*1.5);
        }
        else
        {
            // projEnd = target.Location + (target.Velocity*dist/dxWeapon.ProjectileSpeed);
            if (!ComputeTargetLead(target, projStart, dxWeapon.ProjectileSpeed,
                                   5.0, projEnd))
                return false;
        }
    }
    else
        projEnd = target.Location;

    if (bIsThrown)
        projEnd += vect(0,0,-1)*(target.CollisionHeight-5);

    dist = VSize(projEnd - Location);
    if (dist < 0)
        dist = 0;

    if ((dist < tempMinRange) || (dist-extraDist > tempMaxRange))
        return false;

    if (!bIsThrown)
    {
        bSafe = FastTrace(target.Location, projStart);
        if (!bSafe && target.bIsPlayer)  // players only... hack
        {
            projEnd += vect(0,0,1)*target.BaseEyeHeight;
            bSafe = FastTrace(target.Location + vect(0,0,1)*target.BaseEyeHeight, projStart);
        }
        if (!bSafe)
            return false;
    }

    if (dxWeapon.bInstantHit)
        return (AISafeToShoot(hitActor, projEnd, projStart, , true));
    else
    {
        extent.X = dxWeapon.ProjectileClass.default.CollisionRadius;
        extent.Y = dxWeapon.ProjectileClass.default.CollisionRadius;
        extent.Z = dxWeapon.ProjectileClass.default.CollisionHeight;
        if (bIsThrown && (throwAccuracy > 0))
            return (AISafeToThrow(projEnd, projStart, throwAccuracy,
                                  extent));
        else
            return (AISafeToShoot(hitActor, projEnd, projStart, extent*3));
    }
}


// ----------------------------------------------------------------------
// ComputeTargetLead()
// ----------------------------------------------------------------------

function bool ComputeTargetLead(pawn target, vector projectileStart,
                                float projectileSpeed,
                                float maxTime,
                                out Vector hitPos)
{
    local vector targetLoc;
    local vector targetVel;
    local float  termA, termB, termC;
    local float  temp;
    local float  base, range;
    local float  time1, time2;
    local bool   bSuccess;

    bSuccess = true;

    targetLoc = target.Location - projectileStart;
    targetVel = target.Velocity;
    if (target.Physics == PHYS_Falling)
        targetVel.Z = 0;

    // Given a target position and velocity, and a projectile speed,
    // compute the position at which a projectile will hit the
    // target if the target continues at its current velocity

    // (Warning: messy computations follow.  I can't believe I remembered
    // enough algebra to figure this out on my own... :)

    termA = targetVel.X*targetVel.X +
            targetVel.Y*targetVel.Y +
            targetVel.Z*targetVel.Z -
            projectileSpeed*projectileSpeed;
    termB = 2*targetLoc.X*targetVel.X +
            2*targetLoc.Y*targetVel.Y +
            2*targetLoc.Z*targetVel.Z;
    termC = targetLoc.X*targetLoc.X +
            targetLoc.Y*targetLoc.Y +
            targetLoc.Z*targetLoc.Z;

    if ((termA < 0.000001) && (termA > -0.000001))  // avoid divide-by-zero errors...
        termA = 0.000001;  // fudge a little when velocities are equal
    temp = termB*termB - 4*termA*termC;
    if (temp < 0)
        bSuccess = false;

    if (bSuccess)
    {
        base = -termB/(2*termA);
        range = sqrt(temp)/(2*termA);
        time1 = base+range;
        time2 = base-range;
        if ((time1 > time2) || (time1 < 0))  // best time first
            time1 = time2;
        if ((time1 < 0) || (time1 >= maxTime))
            bSuccess = false;
    }

    if (bSuccess)
        hitPos = target.Location + target.Velocity*time1;

    return (bSuccess);

}


// ----------------------------------------------------------------------
// GetPawnWeaponRanges()
// ----------------------------------------------------------------------

function GetPawnWeaponRanges(Pawn other, out float minRange,
                             out float maxAccurateRange, out float maxRange)
{
    local DeusExWeapon            pawnWeapon;
    local Class<DeusExProjectile> projectileClass;

    pawnWeapon = DeusExWeapon(other.Weapon);
    if (pawnWeapon != None)
    {
        pawnWeapon.GetWeaponRanges(minRange, maxAccurateRange, maxRange);
        if (IsThrownWeapon(pawnWeapon))  // hack
            minRange = 0;
    }
    else
    {
        minRange         = 0;
        maxAccurateRange = other.CollisionRadius;
        maxRange         = maxAccurateRange;
    }

    if (maxAccurateRange > maxRange)
        maxAccurateRange = maxRange;
    if (minRange > maxRange)
        minRange = maxRange;

}


// ----------------------------------------------------------------------
// GetWeaponBestRange()
// ----------------------------------------------------------------------

function GetWeaponBestRange(DeusExWeapon dxWeapon, out float bestRangeMin,
                            out float bestRangeMax)
{
    local float temp;
    local float minRange,   maxRange;
    local float AIMinRange, AIMaxRange;

    if (dxWeapon != None)
    {
        dxWeapon.GetWeaponRanges(minRange, maxRange, temp);
        if (IsThrownWeapon(dxWeapon))  // hack
            minRange = 0;
        AIMinRange = dxWeapon.AIMinRange;
        AIMaxRange = dxWeapon.AIMaxRange;

        if ((AIMinRange > 0) && (AIMinRange >= minRange) && (AIMinRange <= maxRange))
            bestRangeMin = AIMinRange;
        else
            bestRangeMin = minRange;
        if ((AIMaxRange > 0) && (AIMaxRange >= minRange) && (AIMaxRange <= maxRange))
            bestRangeMax = AIMaxRange;
        else
            bestRangeMax = maxRange;

        if (bestRangeMin > bestRangeMax)
            bestRangeMin = bestRangeMax;
    }
    else
    {
        bestRangeMin = 0;
        bestRangeMax = 0;
    }
}


// ----------------------------------------------------------------------
// ReadyForNewEnemy()
// ----------------------------------------------------------------------

function bool ReadyForNewEnemy()
{
    if ((Enemy == None) || (EnemyTimer > 5.0))
        return True;
    else
        return False;
}


// ----------------------------------------------------------------------
// CheckEnemyParams()  [internal use only]
// ----------------------------------------------------------------------

function CheckEnemyParams(Pawn checkPawn,
                          out Pawn bestPawn, out int bestThreatLevel, out float bestDist)
{
    local ScriptedPawn sPawn;
    local bool         bReplace;
    local float        dist;
    local int          threatLevel;
    local bool         bValid;

    bValid = IsValidEnemy(checkPawn);
    if (bValid && (Enemy != checkPawn))
    {
        // Honor cloaking, radar transparency, and other augs if this guy isn't our current enemy
        if (ComputeActorVisibility(checkPawn) < 0.1)
            bValid = false;
    }

    if (bValid)
    {
        sPawn = ScriptedPawn(checkPawn);

        dist = VSize(checkPawn.Location - Location);
        if (checkPawn.IsA('Robot'))
            dist *= 0.5;  // arbitrary
        if (Enemy == checkPawn)
            dist *= 0.75;  // arbitrary

        if (sPawn != None)
        {
            if (sPawn.bAttacking)
            {
                if (sPawn.Enemy == self)
                    threatLevel = 2;
                else
                    threatLevel = 1;
            }
            else if (sPawn.GetStateName() == 'Alerting')
                threatLevel = 3;
            else if ((sPawn.GetStateName() == 'Fleeing') || (sPawn.GetStateName() == 'Burning'))
                threatLevel = 0;
            else if (sPawn.Weapon != None)
                threatLevel = 1;
            else
                threatLevel = 0;
        }
        else  // player
        {
            if (checkPawn.Weapon != None)
                threatLevel = 2;
            else
                threatLevel = 1;
        }

        bReplace = false;
        if (bestPawn == None)
            bReplace = true;
        else if (bestThreatLevel < threatLevel)
            bReplace = true;
        else if (bestDist > dist)
            bReplace = true;

        if (bReplace)
        {
            if ((Enemy == checkPawn) || (AICanSee(checkPawn, , false, false, true, true) > 0))
            {
                bestPawn        = checkPawn;
                bestThreatLevel = threatLevel;
                bestDist        = dist;
            }
        }
    }

}


// ----------------------------------------------------------------------
// FindBestEnemy()
// ----------------------------------------------------------------------

function FindBestEnemy(bool bIgnoreCurrentEnemy)
{
    local Pawn  nextPawn;
    local Pawn  bestPawn;
    local float bestDist;
    local int   bestThreatLevel;
    local float newSeenTime;

    bestPawn        = None;
    bestDist        = 0;
    bestThreatLevel = 0;

    if (!bIgnoreCurrentEnemy && (Enemy != None))
        CheckEnemyParams(Enemy, bestPawn, bestThreatLevel, bestDist);
    foreach RadiusActors(Class'Pawn', nextPawn, 2000)  // arbitrary
        if (enemy != nextPawn)
            CheckEnemyParams(nextPawn, bestPawn, bestThreatLevel, bestDist);

    if (bestPawn != Enemy)
        newSeenTime = 0;
    else
        newSeenTime = EnemyLastSeen;

    SetEnemy(bestPawn, newSeenTime, true);

    EnemyTimer = 0;
}


// ----------------------------------------------------------------------
// ShouldStrafe()
// ----------------------------------------------------------------------

function bool ShouldStrafe()
{
    // This may be overridden from subclasses
    //return (AICanSee(enemy, 1.0, false, true, true, true) > 0);
    return (AICanShoot(enemy, false, false, 0.025, true));
}


// ----------------------------------------------------------------------
// ShouldFlee()
// ----------------------------------------------------------------------

function bool ShouldFlee()
{
    // This may be overridden from subclasses
    if (MinHealth > 0)
    {
        if (Health <= MinHealth)
            return true;
        else if (HealthArmLeft <= 0)
            return true;
        else if (HealthArmRight <= 0)
            return true;
        else if (HealthLegLeft <= 0)
            return true;
        else if (HealthLegRight <= 0)
            return true;
        else
            return false;
    }
    else
        return false;
}


// ----------------------------------------------------------------------
// ShouldDropWeapon()
// ----------------------------------------------------------------------

function bool ShouldDropWeapon()
{
    if (((HealthArmLeft <= 0) || (HealthArmRight <= 0)) && (Health > 0))
        return true;
    else
        return false;
}


// ----------------------------------------------------------------------
// TryLocation()
// ----------------------------------------------------------------------

function bool TryLocation(out vector position, optional float minDist, optional bool bTraceActors,
                          optional NearbyProjectileList projList)
{
    local float   magnitude;
    local vector  normalPos;
    local Rotator rot;
    local float   dist;
    local bool    bSuccess;

    normalPos = position-Location;
    magnitude = VSize(normalPos);
    if (minDist > magnitude)
        minDist = magnitude;
    rot = Rotator(position-Location);
    bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, position);

    if (bSuccess)
    {
        if (bDefendHome && !IsNearHome(position))
            bSuccess = false;
        else if (bAvoidHarm && IsLocationDangerous(projList, position))
            bSuccess = false;
    }

    return (bSuccess);
}


// ----------------------------------------------------------------------
// ComputeBestFiringPosition()
// ----------------------------------------------------------------------

function EDestinationType ComputeBestFiringPosition(out vector newPosition)
{
    local float            selfMinRange, selfMaxRange;
    local float            enemyMinRange, enemyMaxRange;
    local float            temp;
    local float            dist;
    local float            innerRange[2], outerRange[2];
    local Rotator          relativeRotation;
    local float            hAngle, vAngle;
    local int              acrossDist;
    local float            awayDist;
    local float            extraDist;
    local float            fudgeMargin;
    local int              angle;
    local float            maxDist;
    local float            distDelta;
    local bool             bInnerValid, bOuterValid;
    local vector           tryVector;
    local EDestinationType destType;
    local float            moveMult;
    local float            reloadMult;
    local float            minArea;
    local float            minDist;
    local float            range;
    local float            margin;

    local NearbyProjectileList projList;
    local vector               projVector;
    local bool                 bUseProjVector;

    local rotator              sprintRot;
    local vector               sprintVect;
    local bool                 bUseSprint;

    destType = DEST_Failure;

    extraDist   = enemy.CollisionRadius*0.5;
    fudgeMargin = 100;
    minArea     = 35;

    GetPawnWeaponRanges(self, selfMinRange, selfMaxRange, temp);
    GetPawnWeaponRanges(enemy, enemyMinRange, temp, enemyMaxRange);

    if (selfMaxRange > 1200)
        selfMaxRange = 1200;
    if (enemyMaxRange > 1200)
        enemyMaxRange = 1200;

    // hack, to prevent non-strafing NPCs from trying to back up
    if (!bCanStrafe)
        selfMinRange  = 0;

    minDist = enemy.CollisionRadius + CollisionRadius - (extraDist+1);
    if (selfMinRange < minDist)
        selfMinRange = minDist;
    if (selfMinRange < MinRange)
        selfMinRange = MinRange;
    if (selfMaxRange > MaxRange)
        selfMaxRange = MaxRange;

    dist = VSize(enemy.Location-Location);

    innerRange[0] = selfMinRange;
    innerRange[1] = selfMaxRange;
    outerRange[0] = selfMinRange;
    outerRange[1] = selfMaxRange;

    // hack, to prevent non-strafing NPCs from trying to back up

    if (selfMaxRange > enemyMinRange)
        innerRange[1] = enemyMinRange;
    if ((selfMinRange < enemyMaxRange) && bCanStrafe)  // hack, to prevent non-strafing NPCs from trying to back up
        outerRange[0] = enemyMaxRange;

    range = outerRange[1]-outerRange[0];
    if (range < minArea)
    {
        outerRange[0] = 0;
        outerRange[1] = 0;
    }
    range = innerRange[1]-innerRange[0];
    if (range < minArea)
    {
        innerRange[0] = outerRange[0];
        innerRange[1] = outerRange[1];
        outerRange[0] = 0;
        outerRange[1] = 0;
    }

    // If the enemy can reach us through our entire weapon range, just use the range
    if ((innerRange[0] >= innerRange[1]) && (outerRange[0] >= outerRange[1]))
    {
        innerRange[0] = selfMinRange;
        innerRange[1] = selfMaxRange;
    }

    innerRange[0] += extraDist;
    innerRange[1] += extraDist;
    outerRange[0] += extraDist;
    outerRange[1] += extraDist;

    if (innerRange[0] >= innerRange[1])
        bInnerValid = false;
    else
        bInnerValid = true;
    if (outerRange[0] >= outerRange[1])
        bOuterValid = false;
    else
        bOuterValid = true;

    if (!bInnerValid)
    {
        // ugly
        newPosition = Location;
//		return DEST_SameLocation;
        return destType;
    }

    relativeRotation = Rotator(Location - enemy.Location);

    hAngle = (relativeRotation.Yaw - enemy.Rotation.Yaw) & 65535;
    if (hAngle > 32767)
        hAngle -= 65536;
    // ignore vertical angle for now

    awayDist   = dist;
    acrossDist = 0;
    maxDist    = GroundSpeed*0.6;  // distance covered in 6/10 second

    if (bInnerValid)
    {
        margin = (innerRange[1]-innerRange[0]) * 0.5;
        if (margin > fudgeMargin)
            margin = fudgeMargin;
        if (awayDist < innerRange[0])
            awayDist = innerRange[0]+margin;
        else if (awayDist > innerRange[1])
            awayDist = innerRange[1]-margin;
    }
    if (bOuterValid)
    {
        margin = (outerRange[1]-outerRange[0]) * 0.5;
        if (margin > fudgeMargin)
            margin = fudgeMargin;
        if (awayDist > outerRange[1])
            awayDist = outerRange[1]-margin;
    }

    if (awayDist > dist+maxDist)
        awayDist = dist+maxDist;
    if (awayDist < dist-maxDist)
        awayDist = dist-maxDist;

    // Used to determine whether NPCs should sprint/avoid aim
    moveMult = 1.0;
    if ((dist <= 180) && enemy.bIsPlayer && (enemy.Weapon != None) && (enemyMaxRange < 180))
        moveMult = CloseCombatMult;

    if (bAvoidAim && !enemy.bIgnore && (FRand() <= AvoidAccuracy*moveMult))
    {
        if ((awayDist < enemyMaxRange+maxDist+50) && (awayDist < 800) && (Enemy.Weapon != None))
        {
            if (dist > 0)
                angle = int(atan(CollisionRadius*2.0/dist)*32768/Pi);
            else
                angle = 16384;

            if ((hAngle >= -angle) && (hAngle <= angle))
            {
                if (hAngle < 0)
                    acrossDist = (-angle-hAngle)-128;
                else
                    acrossDist = (angle-hAngle)+128;
                if (Rand(20) == 0)
                    acrossDist = -acrossDist;
            }
        }
    }

// projList is implicitly initialized to null...

    bUseProjVector = false;
    if (bAvoidHarm && (FRand() <= HarmAccuracy))
    {
        if (GetProjectileList(projList, Location) > 0)
        {
            if (IsLocationDangerous(projList, Location))
            {
                projVector = ComputeAwayVector(projList);
                bUseProjVector = true;
            }
        }
    }

    reloadMult = 1.0;
    if (IsWeaponReloading() && Enemy.bIsPlayer)
        reloadMult = 0.5;

    bUseSprint = false;
    if (!bUseProjVector && bSprint && bCanStrafe && !enemy.bIgnore && (FRand() <= SprintRate*0.5*moveMult*reloadMult))
    {
        if (bOuterValid || (innerRange[1] > 100))  // sprint on long-range weapons only
        {
            sprintRot = Rotator(enemy.Location - Location);
            if (Rand(2) == 1)
                sprintRot.Yaw += 16384;
            else
                sprintRot.Yaw += 49152;
            sprintRot = RandomBiasedRotation(sprintRot.Yaw, 0.5, 0, 0);
            sprintRot.Pitch = 0;
            sprintVect = Vector(sprintRot)*GroundSpeed*(FRand()+0.5);
            bUseSprint = true;
        }
    }

    if ((acrossDist != 0) || (awayDist != dist) || bUseProjVector || bUseSprint)
    {
        if (Rand(40) != 0)
        {
            if ((destType == DEST_Failure) && bUseProjVector)
            {
                tryVector = projVector + Location;
                if (TryLocation(tryVector, CollisionRadius+16))
                    destType = DEST_NewLocation;
            }
            if ((destType == DEST_Failure) && (acrossDist != 0) && (awayDist != dist))
            {
                tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*awayDist + enemy.Location;
                if (TryLocation(tryVector, CollisionRadius+16, , projList))
                    destType = DEST_NewLocation;
            }
            if ((destType == DEST_Failure) && (awayDist != dist))
            {
                tryVector = Vector(relativeRotation)*awayDist + enemy.Location;
                if (TryLocation(tryVector, CollisionRadius+16, , projList))
                    destType = DEST_NewLocation;
            }
            if ((destType == DEST_Failure) && (acrossDist != 0))
            {
                tryVector = Vector(relativeRotation+(rot(0, 1, 0)*acrossDist))*dist + enemy.Location;
                if (TryLocation(tryVector, CollisionRadius+16, , projList))
                    destType = DEST_NewLocation;
            }
            if ((destType == DEST_Failure) && bUseSprint)
            {
                tryVector = sprintVect + Location;
                if (TryLocation(tryVector, CollisionRadius+16))
                    destType = DEST_NewLocation;
            }
        }
        if (destType == DEST_Failure)
        {
            if ((moveMult >= 0.5) || (FRand() <= moveMult))
            {
                if (AIPickRandomDestination(CollisionRadius+16, maxDist,
                                            relativeRotation.Yaw+32768, 0.6, -relativeRotation.Pitch, 0.6, 2,
                                            0.9, tryVector))
                    if (!bDefendHome || IsNearHome(tryVector))
                        if (!bAvoidHarm || !IsLocationDangerous(projList, tryVector))
                            destType = DEST_NewLocation;
            }
            else
                destType = DEST_SameLocation;
        }
        if (destType != DEST_Failure)
            newPosition = tryVector;
    }
    else
        destType = DEST_SameLocation;

    return destType;
}


// ----------------------------------------------------------------------
// SetAttackAngle()
//
// Sets the angle from which an asynchronous attack will occur
// (hack needed for DeusExWeapon)
// ----------------------------------------------------------------------

function SetAttackAngle()
{
    local bool bCanShoot;

    bCanShoot = false;
    if (Enemy != None)
        if (AICanShoot(Enemy, true, false, 0.025))
            bCanShoot = true;

    if (!bCanShoot)
        ViewRotation = Rotation;
}


// ----------------------------------------------------------------------
// AdjustAim()
//
// Adjust the aim at target
// ----------------------------------------------------------------------

function rotator AdjustAim(float projSpeed, vector projStart, int aimerror, bool leadTarget, bool warnTarget)
{
    local rotator     FireRotation;
    local vector      FireSpot;
    local actor       HitActor;
    local vector      HitLocation, HitNormal;
    local vector      vectorArray[3];
    local vector      tempVector;
    local int         i;
    local int         swap;
    local Rotator     rot;
    local bool        bIsThrown;
    local DeusExMover dxMover;
    local actor       Target;  // evil fix -- STM

    bIsThrown = IsThrownWeapon(DeusExWeapon(Weapon));

// took this line out for evil fix...
//	if ( Target == None )

    Target = Enemy;
    if ( Target == None )
        return Rotation;
    if ( !Target.IsA('Pawn') )
        return rotator(Target.Location - Location);

    FireSpot = Target.Location;
    if (leadTarget && (projSpeed > 0))
    {
        if (bIsThrown)
        {
            // compute target's position 1.5 seconds in the future
            FireSpot = target.Location + (target.Velocity*1.5);
        }
        else
        {
            //FireSpot += (Target.Velocity * VSize(Target.Location - ProjStart)/projSpeed);
            ComputeTargetLead(Pawn(Target), ProjStart, projSpeed, 20.0, FireSpot);
        }
    }

    if (bIsThrown)
    {
        vectorArray[0] = FireSpot - vect(0,0,1)*(Target.CollisionHeight-5);  // floor
        vectorArray[1] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2;
        vectorArray[2] = vectorArray[0] + Vector(rot(0,1,0)*Rand(65536))*CollisionRadius*1.2;

        for (i=0; i<3; i++)
        {
            if (AISafeToThrow(vectorArray[i], ProjStart, 0.025))
                break;
        }
        if (i < 3)
        {
            FireSpot = vectorArray[i];
            FireRotation = ViewRotation;
        }
        else
            FireRotation = Rotator(FireSpot - ProjStart);
    }
    else
    {
        dxMover = DeusExMover(Target.Base);
        if ((dxMover != None) && dxMover.bBreakable)
        {
            tempVector = Normal((Location-Target.Location)*vect(1,1,0))*(Target.CollisionRadius*1.01) -
                         vect(0,0,1)*(Target.CollisionHeight*1.01);
            vectorArray[0] = FireSpot + tempVector;
        }
        else if (bAimForHead)
            vectorArray[0] = FireSpot + vect(0,0,1)*(Target.CollisionHeight*0.85);    // head
        else
            vectorArray[0] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight);
        vectorArray[1] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight);
        vectorArray[2] = FireSpot + vect(0,0,1)*((FRand()*2-1)*Target.CollisionHeight);

        for (i=0; i<3; i++)
        {
            if (AISafeToShoot(HitActor, vectorArray[i], ProjStart))
                break;
        }
        if (i < 3)
            FireSpot = vectorArray[i];

        FireRotation = Rotator(FireSpot - ProjStart);
    }

    if (warnTarget && Pawn(Target) != None) 
        Pawn(Target).WarnTarget(self, projSpeed, vector(FireRotation)); 

    FireRotation.Yaw = FireRotation.Yaw & 65535;
    if ( (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) > 8192)
        && (Abs(FireRotation.Yaw - (Rotation.Yaw & 65535)) < 57343) )
    {
        if ( (FireRotation.Yaw > Rotation.Yaw + 32768) || 
            ((FireRotation.Yaw < Rotation.Yaw) && (FireRotation.Yaw > Rotation.Yaw - 32768)) )
            FireRotation.Yaw = Rotation.Yaw - 8192;
        else
            FireRotation.Yaw = Rotation.Yaw + 8192;
    }
    viewRotation = FireRotation;            
    return FireRotation;
}


// ----------------------------------------------------------------------
// IsThrownWeapon()
// ----------------------------------------------------------------------

function bool IsThrownWeapon(DeusExWeapon testWeapon)
{
    local Class<ThrownProjectile> throwClass;
    local bool                    bIsThrown;

    bIsThrown = false;
    if (testWeapon != None)
    {
        if (!testWeapon.bInstantHit)
        {
            throwClass = class<ThrownProjectile>(testWeapon.ProjectileClass);
            if (throwClass != None)
                bIsThrown = true;
        }
    }

    return bIsThrown;

}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// CALLBACKS AND OVERRIDDEN FUNCTIONS
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// Tick()
// ----------------------------------------------------------------------

function Tick(float deltaTime)
{
    local float        dropPeriod;
    local float        adjustedRate;
    local DeusExPlayer player;
    local name         stateName;
    local vector       loc;
    local bool         bDoLowPriority;
    local bool         bCheckOther;
    local bool         bCheckPlayer;

    player = DeusExPlayer(GetPlayerPawn());

    bDoLowPriority = true;
    bCheckPlayer   = true;
    bCheckOther    = true;
    if (bTickVisibleOnly)
    {
        if (DistanceFromPlayer > 1200)
            bDoLowPriority = false;
        if (DistanceFromPlayer > 2500)
            bCheckPlayer = false;
        if ((DistanceFromPlayer > 600) && (LastRendered() >= 5.0))
            bCheckOther = false;
    }

/*
    if (bDisappear && (InStasis() || (LastRendered() > 5.0)))
    {
        Destroy();
        return;
    }

    if (PrePivotTime > 0)
    {
        if (deltaTime < PrePivotTime)
        {
            PrePivot = PrePivot + (DesiredPrePivot-PrePivot)*deltaTime/PrePivotTime;
            PrePivotTime -= deltaTime;
        }
        else
        {
            PrePivot = DesiredPrePivot;
            PrePivotTime = 0;
        }
    }

    if (bDoLowPriority)
        Super.Tick(deltaTime);

    UpdateAgitation(deltaTime);
    UpdateFear(deltaTime);

    AlarmTimer -= deltaTime;
    if (AlarmTimer < 0)
        AlarmTimer = 0;

    if (Weapon != None)
        WeaponTimer += deltaTime;
    else if (WeaponTimer != 0)
        WeaponTimer = 0;

    if ((ReloadTimer > 0) && (Weapon != None))
        ReloadTimer -= deltaTime;
    else
        ReloadTimer = 0;

    if (AvoidWallTimer > 0)
    {
        AvoidWallTimer -= deltaTime;
        if (AvoidWallTimer < 0)
            AvoidWallTimer = 0;
    }

    if (AvoidBumpTimer > 0)
    {
        AvoidBumpTimer -= deltaTime;
        if (AvoidBumpTimer < 0)
            AvoidBumpTimer = 0;
    }

    if (CloakEMPTimer > 0)
    {
        CloakEMPTimer -= deltaTime;
        if (CloakEMPTimer < 0)
            CloakEMPTimer = 0;
    }

    if (TakeHitTimer > 0)
    {
        TakeHitTimer -= deltaTime;
        if (TakeHitTimer < 0)
            TakeHitTimer = 0;
    }

    if (CarcassCheckTimer > 0)
    {
        CarcassCheckTimer -= deltaTime;
        if (CarcassCheckTimer < 0)
            CarcassCheckTimer = 0;
    }

    if (PotentialEnemyTimer > 0)
    {
        PotentialEnemyTimer -= deltaTime;
        if (PotentialEnemyTimer <= 0)
        {
            PotentialEnemyTimer    = 0;
            PotentialEnemyAlliance = '';
        }
    }

    if (BeamCheckTimer > 0)
    {
        BeamCheckTimer -= deltaTime;
        if (BeamCheckTimer < 0)
            BeamCheckTimer = 0;
    }

    if (FutzTimer > 0)
    {
        FutzTimer -= deltaTime;
        if (FutzTimer < 0)
            FutzTimer = 0;
    }

    if (PlayerAgitationTimer > 0)
    {
        PlayerAgitationTimer -= deltaTime;
        if (PlayerAgitationTimer < 0)
            PlayerAgitationTimer = 0;
    }

    if (DistressTimer >= 0)
    {
        DistressTimer += deltaTime;
        if (DistressTimer > FearSustainTime)
            DistressTimer = -1;
    }

    if (bHasCloak)
        EnableCloak(Health <= CloakThreshold);

    if (bAdvancedTactics)
    {
        if ((Acceleration == vect(0,0,0)) || (Physics != PHYS_Walking) ||
            (TurnDirection == TURNING_None))
        {
            bAdvancedTactics = false;
            if (TurnDirection != TURNING_None)
                MoveTimer -= 4.0;
            ActorAvoiding    = None;
            NextDirection    = TURNING_None;
            TurnDirection    = TURNING_None;
            bClearedObstacle = true;
            ObstacleTimer    = 0;
        }
    }

    if (bOnFire)
    {
        burnTimer += deltaTime;
        if (burnTimer >= BurnPeriod)
            ExtinguishFire();
    }

    if (bDoLowPriority)
    {
        if ((bleedRate > 0) && bCanBleed)
        {
            adjustedRate = (1.0-FClamp(bleedRate, 0.0, 1.0))*1.0+0.1;  // max 10 drops per second
            dropPeriod = adjustedRate / FClamp(VSize(Velocity)/512.0, 0.05, 1.0);
            dropCounter += deltaTime;
            while (dropCounter >= dropPeriod)
            {
                SpurtBlood();
                dropCounter -= dropPeriod;
            }
            bleedRate -= deltaTime/clotPeriod;
        }
        if (bleedRate <= 0)
        {
            dropCounter = 0;
            bleedRate   = 0;
        }
    }
*/

    if (bStandInterpolation)
        UpdateStanding(deltaTime);

    // this is UGLY!
    if (bOnFire && (health > 0))
    {
        stateName = GetStateName();
        if ((stateName != 'Burning') && (stateName != 'TakingHit') && (stateName != 'RubbingEyes'))
            GotoState('Burning');
    }
    else
    {
        if (bDoLowPriority)
        {
            // Don't allow radius-based convos to interupt other conversations!
            if ((player != None) && (GetStateName() != 'Conversation') && (GetStateName() != 'FirstPersonConversation'))
                player.StartConversation(Self, IM_Radius);
        }

        if (CheckEnemyPresence(deltaTime, bCheckPlayer, bCheckOther))
            HandleEnemy();
        else
        {
            CheckBeamPresence(deltaTime);
            if (bDoLowPriority || LastRendered() < 5.0)
                CheckCarcassPresence(deltaTime);  // hacky -- may change state!
        }
    }

    // Randomly spawn an air bubble every 0.2 seconds if we're underwater
    if (HeadRegion.Zone.bWaterZone && bSpawnBubbles && bDoLowPriority)
    {
        swimBubbleTimer += deltaTime;
        if (swimBubbleTimer >= 0.2)
        {
            swimBubbleTimer = 0;
            if (FRand() < 0.4)
            {
                loc = Location + VRand() * 4;
                loc.Z += CollisionHeight * 0.9;
                Spawn(class'AirBubble', Self,, loc);
            }
        }
    }

    // Handle poison damage
    UpdatePoison(deltaTime);
}


// ----------------------------------------------------------------------
// SpurtBlood()
// ----------------------------------------------------------------------

function SpurtBlood()
{
    local vector bloodVector;

    bloodVector = vect(0,0,1)*CollisionHeight*0.5;  // so folks don't bleed from the crotch
    spawn(Class'BloodDrop',,,bloodVector+Location);
}


// ----------------------------------------------------------------------
// TakeDamage()
// ----------------------------------------------------------------------

function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, Vector momentum, name damageType)
{
    TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, true);
}


// ----------------------------------------------------------------------
// Timer()
// ----------------------------------------------------------------------

function Timer()
{
    UpdateFire();
}


// ----------------------------------------------------------------------
// ZoneChange()
// ----------------------------------------------------------------------

function ZoneChange(ZoneInfo newZone)
{
    local vector jumpDir;

    if (!bInWorld)
        return;

    if (newZone.bWaterZone)
    {
        EnableShadow(false);
        if (!bCanSwim)
            MoveTimer = -1.0;
        else if (Physics != PHYS_Swimming)
        {
            if (bOnFire)
                ExtinguishFire();

            PlaySwimming();
            setPhysics(PHYS_Swimming);
        }
        swimBubbleTimer = 0;
    }
    else if (Physics == PHYS_Swimming)
    {
        EnableShadow(true);
        if ( bCanFly )
             SetPhysics(PHYS_Flying); 
        else
        { 
            SetPhysics(PHYS_Falling);
            if ( bCanWalk && (Abs(Acceleration.X) + Abs(Acceleration.Y) > 0) && CheckWaterJump(jumpDir) )
                JumpOutOfWater(jumpDir);
        }
    }
}


// ----------------------------------------------------------------------
// BaseChange()
// ----------------------------------------------------------------------

singular function BaseChange()
{
    Super.BaseChange();

    if (bSitting && !IsSeatValid(SeatActor))
    {
        StandUp();
        if (GetStateName() == 'Sitting')
            GotoState('Sitting', 'Begin');
    }
}


// ----------------------------------------------------------------------
// PainTimer()
// ----------------------------------------------------------------------

event PainTimer()
{
    local float       depth;
    local PointRegion painRegion;

    if ((Health <= 0) || (Level.NetMode == NM_Client))
        return;

    painRegion = HeadRegion;
    if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType))
        painRegion = Region;
    if (!painRegion.Zone.bPainZone || (painRegion.Zone.DamageType == ReducedDamageType))
        painRegion = FootRegion;

    if (painRegion.Zone.bPainZone && (painRegion.Zone.DamageType != ReducedDamageType))
    {
        depth = 0;
        if (FootRegion.Zone.bPainZone)
            depth += 0.3;
        if (Region.Zone.bPainZone)
            depth += 0.3;
        if (HeadRegion.Zone.bPainZone)
            depth += 0.4;

        if (painRegion.Zone.DamagePerSec > 0)
            TakeDamage(int(float(painRegion.Zone.DamagePerSec) * depth), None, Location, vect(0,0,0), painRegion.Zone.DamageType);
        // took out healing for NPCs -- we don't use healing zones anyway
        /*
        else if ( Health < Default.Health )
            Health = Min(Default.Health, Health - depth * FootRegion.Zone.DamagePerSec);
        */

        if (Health > 0)
            PainTime = 1.0;
    }
    else if (HeadRegion.Zone.bWaterZone && (UnderWaterTime > 0))
    {
        TakeDamage(5, None, Location, vect(0,0,0), 'Drowned');
        if (Health > 0)
            PainTime = 2.0;
    }
}

// ----------------------------------------------------------------------
// CheckWaterJump()
// ----------------------------------------------------------------------

function bool CheckWaterJump(out vector WallNormal)
{
    local actor HitActor;
    local vector HitLocation, HitNormal, checkpoint, start, checkNorm, Extent;

    if (CarriedDecoration != None)
        return false;
    checkpoint = vector(Rotation);
    checkpoint.Z = 0.0;
    checkNorm = Normal(checkpoint);
    checkPoint = Location + CollisionRadius * checkNorm;
    Extent = CollisionRadius * vect(1,1,0);
    Extent.Z = CollisionHeight;
    HitActor = Trace(HitLocation, HitNormal, checkpoint, Location, false, Extent);
    if ( (HitActor != None) && (Pawn(HitActor) == None) )
    {
        WallNormal = -1 * HitNormal;
        start = Location;
        start.Z += 1.1 * MaxStepHeight + CollisionHeight;
        checkPoint = start + 2 * CollisionRadius * checkNorm;
        HitActor = Trace(HitLocation, HitNormal, checkpoint, start, true, Extent);
        if (HitActor == None)
            return true;
    }

    return false;
}


// ----------------------------------------------------------------------
// SetMovementPhysics()
// ----------------------------------------------------------------------

function SetMovementPhysics()
{
    // re-implement SetMovementPhysics() in subclass for flying and swimming creatures
    if (Physics == PHYS_Falling)
        return;

    if (Region.Zone.bWaterZone && bCanSwim)
        SetPhysics(PHYS_Swimming);
    else if (Default.Physics == PHYS_None)
        SetPhysics(PHYS_Walking);
    else
        SetPhysics(Default.Physics);
}


// ----------------------------------------------------------------------
// PreSetMovement()
// ----------------------------------------------------------------------

function PreSetMovement()
{
    // Copied from Pawn.uc and overridden so Pawn doesn't erase our walking/swimming/flying physics
    if (JumpZ > 0)
        bCanJump = true;
    // No, no, no!!!
    /*
    bCanWalk = true;
    bCanSwim = false;
    bCanFly = false;
    MinHitWall = -0.6;
    if (Intelligence > BRAINS_Reptile)
        bCanOpenDoors = true;
    if (Intelligence == BRAINS_Human)
        bCanDoSpecial = true;
    */
}


// ----------------------------------------------------------------------
// ChangedWeapon()
// ----------------------------------------------------------------------

function ChangedWeapon()
{
    // do nothing
}


// ----------------------------------------------------------------------
// SwitchToBestWeapon()
// ----------------------------------------------------------------------

function bool SwitchToBestWeapon()
{
    local Inventory    inv;
    local DeusExWeapon curWeapon;
    local float        score;
    local DeusExWeapon dxWeapon;
    local DeusExWeapon bestWeapon;
    local float        bestScore;
    local int          fallbackLevel;
    local int          curFallbackLevel;
    local bool         bBlockSpecial;
    local bool         bValid;
    local bool         bWinner;
    local float        minRange, accRange;
    local float        range, centerRange;
    local float        cutoffRange;
    local float        enemyRange;
    local float        minEnemy, accEnemy, maxEnemy;
    local ScriptedPawn enemyPawn;
    local Robot        enemyRobot;
    local DeusExPlayer enemyPlayer;
    local float        enemyRadius;
    local bool         bEnemySet;
    local int          loopCount, i;  // hack - check for infinite inventory
    local Inventory    loopInv;       // hack - check for infinite inventory

    if (ShouldDropWeapon())
    {
        DropWeapon();
        return false;
    }

    bBlockSpecial = false;
    dxWeapon = DeusExWeapon(Weapon);
    if (dxWeapon != None)
    {
        if (dxWeapon.AITimeLimit > 0)
        {
            if (SpecialTimer <= 0)
            {
                bBlockSpecial = true;
                FireTimer = dxWeapon.AIFireDelay;
            }
        }
    }

    bestWeapon      = None;
    bestScore       = 0;
    fallbackLevel   = 0;
    inv             = Inventory;

    bEnemySet   = false;
    minEnemy    = 0;
    accEnemy    = 0;
    enemyRange  = 400;  // default
    enemyRadius = 0;
    enemyPawn   = None;
    enemyRobot  = None;
    if (Enemy != None)
    {
        bEnemySet   = true;
        enemyRange  = VSize(Enemy.Location - Location);
        enemyRadius = Enemy.CollisionRadius;
        if (DeusExWeapon(Enemy.Weapon) != None)
            DeusExWeapon(Enemy.Weapon).GetWeaponRanges(minEnemy, accEnemy, maxEnemy);
        enemyPawn   = ScriptedPawn(Enemy);
        enemyRobot  = Robot(Enemy);
        enemyPlayer = DeusExPlayer(Enemy);
    }

    loopCount = 0;
    while (inv != None)
    {
        // THIS IS A MAJOR HACK!!!
        loopCount++;
        if (loopCount == 9999)
        {
            log("********** RUNAWAY LOOP IN SWITCHTOBESTWEAPON ("$self$") **********");
            loopInv = Inventory;
            i = 0;
            while (loopInv != None)
            {
                i++;
                if (i > 300)
                    break;
                log("   Inventory "$i$" - "$loopInv);
                loopInv = loopInv.Inventory;
            }
        }

        curWeapon = DeusExWeapon(inv);
        if (curWeapon != None)
        {
            bValid = true;
            if (curWeapon.ReloadCount > 0)
            {
                if (curWeapon.AmmoType == None)
                    bValid = false;
                else if (curWeapon.AmmoType.AmmoAmount < 1)
                    bValid = false;
            }

            // Ensure we can actually use this weapon here
            if (bValid)
            {
                // lifted from DeusExWeapon...
                if ((curWeapon.EnviroEffective == ENVEFF_Air) || (curWeapon.EnviroEffective == ENVEFF_Vacuum) ||
                    (curWeapon.EnviroEffective == ENVEFF_AirVacuum))
                    if (curWeapon.Region.Zone.bWaterZone)
                        bValid = false;
            }

            if (bValid)
            {
                GetWeaponBestRange(curWeapon, minRange, accRange);
                cutoffRange = minRange+(CollisionRadius+enemyRadius);
                range = (accRange - minRange) * 0.5;
                centerRange = minRange + range;
                if (range < 50)
                    range = 50;
                if (enemyRange < centerRange)
                    score = (centerRange - enemyRange)/range;
                else
                    score = (enemyRange - centerRange)/range;
                if ((minRange >= minEnemy) && (accRange <= accEnemy))
                    score += 0.5;  // arbitrary
                if ((cutoffRange >= enemyRange-CollisionRadius) && (cutoffRange >= 256)) // do not use long-range weapons on short-range targets
                    score += 10000;

                curFallbackLevel = 3;
                if (curWeapon.bFallbackWeapon && !bUseFallbackWeapons)
                    curFallbackLevel = 2;
                if (!bEnemySet && !curWeapon.bUseAsDrawnWeapon)
                    curFallbackLevel = 1;
                if ((curWeapon.AIFireDelay > 0) && (FireTimer > 0))
                    curFallbackLevel = 0;
                if (bBlockSpecial && (curWeapon.AITimeLimit > 0) && (SpecialTimer <= 0))
                    curFallbackLevel = 0;

                // Adjust score based on opponent and damage type.
                // All damage types are listed here, even the ones that aren't used by weapons... :)
                // (hacky...)

                switch (curWeapon.WeaponDamageType())
                {
                    case 'Exploded':
                        // Massive explosions are always good
                        score -= 0.2;
                        break;

                    case 'Stunned':
                        if (enemyPawn != None)
                        {
                            if (enemyPawn.bStunned)
                                score += 1000;
                            else
                                score -= 1.5;
                        }
                        if (enemyPlayer != None)
                            score += 10;
                        break;

                    case 'TearGas':
                        if (enemyPawn != None)
                        {
                            if (enemyPawn.bStunned)
                                //score += 1000;
                                bValid = false;
                            else
                                score -= 5.0;
                        }
                        if (enemyRobot != None)
                            //score += 10000;
                            bValid = false;
                        break;

                    case 'HalonGas':
                        if (enemyPawn != None)
                        {
                            if (enemyPawn.bStunned)
                                //score += 1000;
                                bValid = false;
                            else if (enemyPawn.bOnFire)
                                //score += 10000;
                                bValid = false;
                            else
                                score -= 3.0;
                        }
                        if (enemyRobot != None)
                            //score += 10000;
                            bValid = false;
                        break;

                    case 'PoisonGas':
                    case 'Poison':
                    case 'PoisonEffect':
                    case 'Radiation':
                        if (enemyRobot != None)
                            //score += 10000;
                            bValid = false;
                        break;

                    case 'Burned':
                    case 'Flamed':
                    case 'Shot':
                        if (enemyRobot != None)
                            score += 0.5;
                        break;

                    case 'Sabot':
                        if (enemyRobot != None)
                            score -= 0.5;
                        break;

                    case 'EMP':
                    case 'NanoVirus':
                        if (enemyRobot != None)
                            score -= 5.0;
                        else if (enemyPlayer != None)
                            score += 5.0;
                        else
                            //score += 10000;
                            bValid = false;
                        break;

                    case 'Drowned':
                    default:
                        break;
                }

                // Special case for current weapon
                if ((curWeapon == Weapon) && (WeaponTimer < 10.0))
                {
                    // If we last changed weapons less than five seconds ago,
                    // keep this weapon
                    if (WeaponTimer < 5.0)
                        score = -10;

                    // If between five and ten seconds, use a sliding scale
                    else
                        score -= (10.0 - WeaponTimer)/5.0;
                }

                // Throw a little randomness into the computation...
                else
                {
                    score += FRand()*0.1 - 0.05;
                    if (score < 0)
                        score = 0;
                }

                if (bValid)
                {
                    // ugly
                    if (bestWeapon == None)
                        bWinner = true;
                    else if (curFallbackLevel > fallbackLevel)
                        bWinner = true;
                    else if (curFallbackLevel < fallbackLevel)
                        bWinner = false;
                    else if (bestScore > score)
                        bWinner = true;
                    else
                        bWinner = false;
                    if (bWinner)
                    {
                        bestScore     = score;
                        bestWeapon    = curWeapon;
                        fallbackLevel = curFallbackLevel;
                    }
                }
            }
        }
        inv = inv.Inventory;
    }

    // If we're changing weapons, reset the weapon timers
    if (Weapon != bestWeapon)
    {
        if (!bEnemySet)
            WeaponTimer = 10;  // hack
        else
            WeaponTimer = 0;
        if (bestWeapon != None)
            if (bestWeapon.AITimeLimit > 0)
                SpecialTimer = bestWeapon.AITimeLimit;
        ReloadTimer = 0;
    }

    SetWeapon(bestWeapon);

    return false;
}


// ----------------------------------------------------------------------
// LoopBaseConvoAnim()
// ----------------------------------------------------------------------

function LoopBaseConvoAnim()
{
    // Special case for sitting
    if (bSitting)
    {
        if (!IsAnimating())
            PlaySitting();
    }

    // Special case for dancing
    else if (bDancing)
    {
        if (!IsAnimating())
            PlayDancing();
    }

    // Otherwise, just do the usual shit
    else
        Super.LoopBaseConvoAnim();

}


// ----------------------------------------------------------------------
// BackOff()
// ----------------------------------------------------------------------

function BackOff()
{
    SetNextState(GetStateName(), 'ContinueFromDoor');  // MASSIVE hackage
    SetState('BackingOff');
}


// ----------------------------------------------------------------------
// CheckDestLoc()
// ----------------------------------------------------------------------

function CheckDestLoc(vector newDestLoc, optional bool bPathnode)
{
    if (VSize(newDestLoc-LastDestLoc) <= 16)  // too close
        DestAttempts++;
    else if (!IsPointInCylinder(self, newDestLoc))
        DestAttempts++;
    else if (bPathnode && (VSize(newDestLoc-LastDestPoint) <= 16))  // too close
        DestAttempts++;
    else
        DestAttempts = 0;
    LastDestLoc = newDestLoc;
    if (bPathnode && (DestAttempts == 0))
        LastDestPoint = newDestLoc;

    if (bEnableCheckDest && (DestAttempts >= 4))
        BackOff();
}


// ----------------------------------------------------------------------
// ResetDestLoc()
// ----------------------------------------------------------------------

function ResetDestLoc()
{
    DestAttempts  = 0;
    LastDestLoc   = vect(9999,9999,9999);  // hack
    LastDestPoint = LastDestLoc;
}


// ----------------------------------------------------------------------
// EnableCheckDestLoc()
// ----------------------------------------------------------------------

function EnableCheckDestLoc(bool bEnable)
{
//	if (bEnableCheckDest && !bEnable)
        ResetDestLoc();
    bEnableCheckDest = bEnable;
}


// ----------------------------------------------------------------------
// HandleTurn()
// ----------------------------------------------------------------------

function bool HandleTurn(actor Other)
{
    local bool             bHandle;
    local bool             bHackState;
    local DeusExDecoration dxDecoration;
    local ScriptedPawn     scrPawn;

    // THIS ENTIRE SECTION IS A MASSIVE HACK TO GET AROUND PATHFINDING PROBLEMS
    // WHEN AN OBSTACLE COMPLETELY BLOCKS AN NPC'S PATH...

    bHandle    = true;
    bHackState = false;
    if (bEnableCheckDest)
    {
        if (DestAttempts >= 2)
        {
            dxDecoration = DeusExDecoration(Other);
            scrPawn      = ScriptedPawn(Other);
            if (dxDecoration != None)
            {
                if (!dxDecoration.bInvincible && !dxDecoration.bExplosive)
                {
                    dxDecoration.HitPoints = 0;
                    dxDecoration.TakeDamage(1, self, dxDecoration.Location, vect(0,0,0), 'Kick');
                    bHandle = false;
                }
                else if (DestAttempts >= 3)
                {
                    bHackState = true;
                    bHandle    = false;
                }
            }
            else if (scrPawn != None)
            {
                if (DestAttempts >= 3)
                {
                    if (scrPawn.GetStateName() != 'BackingOff')
                    {
                        bHackState = true;
                        bHandle    = false;
                    }
                }
            }
        }

        if (bHackState)
            BackOff();
    }

    return (bHandle);
}


// ----------------------------------------------------------------------
// Bump()
// ----------------------------------------------------------------------

function Bump(actor Other)
{
    local Rotator      rot1, rot2;
    local int          yaw;
    local ScriptedPawn avoidPawn;
    local DeusExPlayer dxPlayer;
    local bool         bTurn;

    // Handle futzing and projectiles
    if (Other.Physics == PHYS_Falling)
    {
        if (DeusExProjectile(Other) != None)
            ReactToProjectiles(Other);
        else
        {
            dxPlayer = DeusExPlayer(Other.Instigator);
            if ((Other != dxPlayer) && (dxPlayer != None))
                ReactToFutz();
        }
    }
    
    // Have we walked into another (non-level) actor?
    bTurn = false;
    if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround && (Other != Level) && !Other.IsA('Mover'))
        if ((TurnDirection == TURNING_None) || (AvoidBumpTimer <= 0))
            if (HandleTurn(Other))
                bTurn = true;

    // Turn away from the actor
    if (bTurn)
    {
        // If we're not already turning, start
        if (TurnDirection == TURNING_None)
        {
            // Give ourselves a little extra time
            MoveTimer += 4.0;

            rot1 = Rotator(Other.Location-Location);  // direction of object being bumped
            rot2 = Rotator(Acceleration);  // direction we wish to go
            yaw  = (rot2.Yaw - rot1.Yaw) & 65535;
            if (yaw > 32767)
                yaw -= 65536;

            // Depending on the angle we bump the actor, turn left or right
            if (yaw < 0)
            {
                TurnDirection = TURNING_Left;
                NextDirection = TURNING_Right;
            }
            else
            {
                TurnDirection = TURNING_Right;
                NextDirection = TURNING_Left;
            }
            bClearedObstacle = false;

            // Enable AlterDestination()
            bAdvancedTactics = true;
        }

        // Ignore multiple bumps in a row
        // BOOGER! Ignore same bump actor?
        if (AvoidBumpTimer <= 0)
        {
            AvoidBumpTimer   = 0.2;
            ActorAvoiding    = Other;
            bClearedObstacle = false;

            avoidPawn = ScriptedPawn(ActorAvoiding);

            // Avoid pairing off
            if (avoidPawn != None)
            {
                if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) &&
                    (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self))
                {
                    if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right))
                    {
                        TurnDirection = TURNING_Left;
                        if (NextDirection != TURNING_None)
                            NextDirection = TURNING_Right;
                    }
                    else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left))
                    {
                        TurnDirection = TURNING_Right;
                        if (NextDirection != TURNING_None)
                            NextDirection = TURNING_Left;
                    }
                }
            }
        }
    }
}


// ----------------------------------------------------------------------
// HitWall()
// ----------------------------------------------------------------------

function HitWall(vector HitLocation, Actor hitActor)
{
    local ScriptedPawn avoidPawn;

    // We only care about HitWall as it pertains to level geometry
    if (hitActor != Level)
        return;

    // Are we walking?
    if ((Physics == PHYS_Walking) && (Acceleration != vect(0,0,0)) && bWalkAround &&
        (AvoidWallTimer <= 0))
    {
        // Are we turning?
        if (TurnDirection != TURNING_None)
        {
            AvoidWallTimer = 1.0;

            // About face
            TurnDirection    = NextDirection;
            NextDirection    = TURNING_None;
            bClearedObstacle = false;

            // Avoid pairing off
            avoidPawn = ScriptedPawn(ActorAvoiding);
            if (avoidPawn != None)
            {
                if ((avoidPawn.Acceleration != vect(0,0,0)) && (avoidPawn.Physics == PHYS_Walking) &&
                    (avoidPawn.TurnDirection != TURNING_None) && (avoidPawn.ActorAvoiding == self))
                {
                    if ((avoidPawn.TurnDirection == TURNING_Left) && (TurnDirection == TURNING_Right))
                        TurnDirection = TURNING_None;
                    else if ((avoidPawn.TurnDirection == TURNING_Right) && (TurnDirection == TURNING_Left))
                        TurnDirection = TURNING_None;
                }
            }

            // Stopped turning?  Shut down
            if (TurnDirection == TURNING_None)
            {
                ActorAvoiding = None;
                bAdvancedTactics = false;
                MoveTimer -= 4.0;
                ObstacleTimer = 0;
            }
        }
    }
}


// ----------------------------------------------------------------------
// AlterDestination()
// ----------------------------------------------------------------------

function AlterDestination()
{
    local Rotator  dir;
    local int      avoidYaw;
    local int      destYaw;
    local int      moveYaw;
    local int      angle;
    local bool     bPointInCylinder;
    local float    dist1, dist2;
    local bool     bAround;
    local vector   tempVect;
    local ETurning oldTurnDir;

    oldTurnDir = TurnDirection;

    // Sanity check -- are we done walking around the actor?
    if (TurnDirection != TURNING_None)
    {
        if (!bWalkAround)
            TurnDirection = TURNING_None;
        else if (bClearedObstacle)
            TurnDirection = TURNING_None;
        else if (ActorAvoiding == None)
            TurnDirection = TURNING_None;
        else if (ActorAvoiding.bDeleteMe)
            TurnDirection = TURNING_None;
        else if (!IsPointInCylinder(ActorAvoiding, Location,
                                    CollisionRadius*2, CollisionHeight*2))
            TurnDirection = TURNING_None;
    }

    // Are we still turning?
    if (TurnDirection != TURNING_None)
    {
        bAround = false;

        // Is our destination point inside the actor we're walking around?
        bPointInCylinder = IsPointInCylinder(ActorAvoiding, Destination,
                                             CollisionRadius-8, CollisionHeight-8);
        if (bPointInCylinder)
        {
            dist1 = VSize((Location - ActorAvoiding.Location)*vect(1,1,0));
            dist2 = VSize((Location - Destination)*vect(1,1,0));

            // Are we on the right side of the actor?
            if (dist1 > dist2)
            {
                // Just make a beeline, if possible
                tempVect = Destination - ActorAvoiding.Location;
                tempVect.Z = 0;
                tempVect = Normal(tempVect) * (ActorAvoiding.CollisionRadius + CollisionRadius);
                if (tempVect == vect(0,0,0))
                    Destination = Location;
                else
                {
                    tempVect += ActorAvoiding.Location;
                    tempVect.Z = Destination.Z;
                    Destination = tempVect;
                }
            }
            else
                bAround = true;
        }
        else
            bAround = true;

        // We have a valid destination -- continue to walk around
        if (bAround)
        {
            // Determine the destination-self-obstacle angle
            dir      = Rotator(ActorAvoiding.Location-Location);
            avoidYaw = dir.Yaw;
            dir      = Rotator(Destination-Location);
            destYaw  = dir.Yaw;

            if (TurnDirection == TURNING_Left)
                angle = (avoidYaw - destYaw) & 65535;
            else
                angle = (destYaw - avoidYaw) & 65535;
            if (angle < 0)
                angle += 65536;

            // If the angle is between 90 and 180 degrees, we've cleared the obstacle
            if (bPointInCylinder || (angle < 16384) || (angle > 32768))  // haven't cleared the actor yet
            {
                if (TurnDirection == TURNING_Left)
                    moveYaw = avoidYaw - 16384;
                else
                    moveYaw = avoidYaw + 16384;
                Destination = Location + Vector(rot(0,1,0)*moveYaw)*400;
            }
            else  // cleared the actor -- move on
                TurnDirection = TURNING_None;
        }
    }

    if (TurnDirection == TURNING_None)
    {
        if (ObstacleTimer > 0)
        {
            TurnDirection = oldTurnDir;
            bClearedObstacle = true;
        }
    }
    else
        ObstacleTimer = 1.5;

    // Reset if done turning
    if (TurnDirection == TURNING_None)
    {
        NextDirection    = TURNING_None;
        ActorAvoiding    = None;
        bAdvancedTactics = false;
        ObstacleTimer    = 0;
        bClearedObstacle = true;

        if (oldTurnDir != TURNING_None)
            MoveTimer -= 4.0;
    }
}


// ----------------------------------------------------------------------
// Frob()
// ----------------------------------------------------------------------

function Frob(Actor Frobber, Inventory frobWith)
{
    Super.Frob(Frobber, frobWith);

    // Check to see if the Frobber is the player.  If so, then
    // check to see if we need to start a conversation.

    if (DeusExPlayer(Frobber) != None)
    {
        if (DeusExPlayer(Frobber).StartConversation(Self, IM_Frob))
        {
            ConversationActor = Pawn(Frobber);
            return;
        }
    }
}


// ----------------------------------------------------------------------
// StopBlendAnims()
// ----------------------------------------------------------------------

function StopBlendAnims()
{
    AIAddViewRotation = rot(0, 0, 0);
    Super.StopBlendAnims();
    PlayTurnHead(LOOK_Forward, 1.0, 1.0);
}


// ----------------------------------------------------------------------
// Falling()
// ----------------------------------------------------------------------

singular function Falling()
{
    if (bCanFly)
    {
        SetPhysics(PHYS_Flying);
        return;
    }
    // SetPhysics(PHYS_Falling); //note - done by default in physics
    if (health > 0)
        SetFall();
}


// ----------------------------------------------------------------------
// SetFall()
// ----------------------------------------------------------------------

function SetFall()
{
    GotoState('FallingState');
}


// ----------------------------------------------------------------------
// LongFall()
// ----------------------------------------------------------------------

function LongFall()
{
    SetFall();
    GotoState('FallingState', 'LongFall');
}


// ----------------------------------------------------------------------
// HearNoise()
// ----------------------------------------------------------------------

function HearNoise(float Loudness, Actor NoiseMaker)
{
    // Do nothing
}


// ----------------------------------------------------------------------
// SeePlayer()
// ----------------------------------------------------------------------

function SeePlayer(Actor SeenPlayer)
{
    // Do nothing
}


// ----------------------------------------------------------------------
// Trigger()
// ----------------------------------------------------------------------

function Trigger( actor Other, pawn EventInstigator )
{
    // Do nothing
}


// ----------------------------------------------------------------------
// Reloading()
// ----------------------------------------------------------------------

function Reloading(DeusExWeapon reloadWeapon, float reloadTime)
{
    if (reloadWeapon == Weapon)
        ReloadTimer = reloadTime;
}


// ----------------------------------------------------------------------
// DoneReloading()
// ----------------------------------------------------------------------

function DoneReloading(DeusExWeapon reloadWeapon)
{
    if (reloadWeapon == Weapon)
        ReloadTimer = 0;
}


// ----------------------------------------------------------------------
// IsWeaponReloading()
// ----------------------------------------------------------------------

function bool IsWeaponReloading()
{
    return (ReloadTimer >= 0.3);
}


// ----------------------------------------------------------------------
// HandToHandAttack()
// ----------------------------------------------------------------------

function HandToHandAttack()
{
    local DeusExWeapon dxWeapon;

    dxWeapon = DeusExWeapon(Weapon);
    if (dxWeapon != None)
        dxWeapon.OwnerHandToHandAttack();
}


// ----------------------------------------------------------------------
// Killed()
// ----------------------------------------------------------------------

function Killed(pawn Killer, pawn Other, name damageType)
{
    if ((Enemy == Other) && (Other != None) && !Other.bIsPlayer)
        Enemy = None;
}


// ----------------------------------------------------------------------
// Died()
// ----------------------------------------------------------------------

function Died(pawn Killer, name damageType, vector HitLocation)
{
    local DeusExPlayer player;
    local name flagName;

    // Set a flag when NPCs die so we can possibly check it later
    player = DeusExPlayer(GetPlayerPawn());

    ExtinguishFire();

    // set the instigator to be the killer
    Instigator = Killer;

    if (player != None)
    {
        // Abort any conversation we may have been having with the NPC
        if (bInConversation)
            player.AbortConversation();

        // Abort any barks we may have been playing
        if (player.BarkManager != None)
            player.BarkManager.ScriptedPawnDied(Self);
    }

    Super.Died(Killer, damageType, HitLocation);  // bStunned is set here

    if (player != None)
    {
        if (bImportant)
        {
            flagName = player.rootWindow.StringToName(BindName$"_Dead");
            player.flagBase.SetBool(flagName, True);

            // make sure the flag never expires
            player.flagBase.SetExpiration(flagName, FLAG_Bool, 0);

            if (bStunned)
            {
                flagName = player.rootWindow.StringToName(BindName$"_Unconscious");
                player.flagBase.SetBool(flagName, True);

                // make sure the flag never expires
                player.flagBase.SetExpiration(flagName, FLAG_Bool, 0);
            }
        }
    }
}



// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// STATES
// ----------------------------------------------------------------------

// ----------------------------------------------------------------------
// state StartUp
//
// Initial state
// ----------------------------------------------------------------------

auto state StartUp
{
    function BeginState()
    {
        bInterruptState = true;
        bCanConverse = false;

        SetMovementPhysics(); 
        if (Physics == PHYS_Walking)
            SetPhysics(PHYS_Falling);

        bStasis = False;
        SetDistress(false);
        BlockReactions();
        ResetDestLoc();
    }

    function EndState()
    {
        bCanConverse = true;
        bStasis = True;
        ResetReactions();
    }

    function Tick(float deltaSeconds)
    {
        Global.Tick(deltaSeconds);
        if (LastRendered() <= 1.0)
        {
            PlayWaiting();
            InitializePawn();
            FollowOrders();
        }
    }

Begin:
    InitializePawn();

    Sleep(FRand()+0.2);
    WaitForLanding();

Start:
    FollowOrders();
}


// ----------------------------------------------------------------------
// state Paralyzed
//
// Do nothing -- ignore all
// (this state lets ViewModel work correctly)
// ----------------------------------------------------------------------

state Paralyzed
{
    ignores bump, frob, reacttoinjury;
    function BeginState()
    {
        StandUp();
        BlockReactions(true);
        bCanConverse = False;
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }
    function EndState()
    {
        ResetReactions();
        bCanConverse = True;
    }

Begin:
    Acceleration=vect(0,0,0);
    PlayAnimPivot('Still');
}


// ----------------------------------------------------------------------
// state Idle
//
// Do nothing
// ----------------------------------------------------------------------

state Idle
{
    ignores bump, frob, reacttoinjury;
    function BeginState()
    {
        StandUp();
        BlockReactions(true);
        bCanConverse = False;
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }
    function EndState()
    {
        ResetReactions();
        bCanConverse = True;
    }

Begin:
    Acceleration=vect(0,0,0);
    DesiredRotation=Rotation;
    PlayAnimPivot('Still');

Idle:
}


// ----------------------------------------------------------------------
// state Conversation
//
// Just sit here until the conversation is over
// ----------------------------------------------------------------------

state Conversation
{
    function Tick(float deltaTime)
    {
        Global.Tick(deltaTime);

        LipSynch(deltaTime);

        // Keep turning towards the person we're speaking to
        if (ConversationActor != None)
        {
            if (bSitting)
            {
                if (SeatActor != None)
                    LookAtActor(ConversationActor, true, true, true, 0, 0.5, SeatActor.Rotation.Yaw+49152, 5461);
                else
                    LookAtActor(ConversationActor, false, true, true, 0, 0.5);
            }
            else
                LookAtActor(ConversationActor, true, true, true, 0, 0.5);
        }
    }

    function LoopHeadConvoAnim()
    {
    }

    function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate)
    {
        local DeusExPlayer dxPlayer;

        dxPlayer = DeusExPlayer(GetPlayerPawn());
        if (dxPlayer != None)
            if (dxPlayer.ConPlay != None)
                if (dxPlayer.ConPlay.GetForcePlay())
                {
                    Global.SetOrders(orderName, newOrderTag, bImmediate);
                    return;
                }

        ConvOrders   = orderName;
        ConvOrderTag = newOrderTag;
    }

    function FollowOrders(optional bool bDefer)
    {
        local name tempConvOrders, tempConvOrderTag;

        // hack
        tempConvOrders   = ConvOrders;
        tempConvOrderTag = ConvOrderTag;
        ResetConvOrders();  // must do this before calling SetOrders(), or recursion will result

        if (tempConvOrders != '')
            Global.SetOrders(tempConvOrders, tempConvOrderTag, true);
        else
            Global.FollowOrders(bDefer);
    }

    function BeginState()
    {
        local DeusExPlayer dxPlayer;
        local bool         bBlock;

        ResetConvOrders();
        EnableCheckDestLoc(false);

        bBlock = True;
        dxPlayer = DeusExPlayer(GetPlayerPawn());
        if (dxPlayer != None)
            if (dxPlayer.ConPlay != None)
                if (dxPlayer.ConPlay.CanInterrupt())
                    bBlock = False;

        bInterruptState = True;
        if (bBlock)
        {
            bCanConverse = False;
            MakePawnIgnored(true);
            BlockReactions(true);
        }
        else
        {
            bCanConverse = True;
            MakePawnIgnored(true);
            BlockReactions(false);
        }

        // Check if the current state is "WaitingFor", "RunningTo" or "GoingTo", in which case
        // we want the orders to be 'Standing' after the conversation is over.  UNLESS the 
        // ScriptedPawn was going somewhere else (OrderTag != '')

        if (((Orders == 'WaitingFor') || (Orders == 'RunningTo') || (Orders == 'GoingTo')) && (OrderTag == ''))
            SetOrders('Standing');

        bConversationEndedNormally = False;
        bInConversation = True;
        bStasis = False;
        SetDistress(false);
        SeekPawn = None;
    }

    function EndState()
    {
        local DeusExPlayer player;
        local bool         bForcePlay;

        bForcePlay = false;
        player = DeusExPlayer(GetPlayerPawn());
        if (player != None)
            if (player.conPlay != None)
                bForcePlay = player.conPlay.GetForcePlay();

        bConvEndState = true;
        if (!bForcePlay && (bConversationEndedNormally != True))
            player.AbortConversation();
        bConvEndState = false;
        ResetConvOrders();

        StopBlendAnims();
        bInterruptState = true;
        bCanConverse    = True;
        MakePawnIgnored(false);
        ResetReactions();
        bStasis = True;
        ConversationActor = None;
    }

Begin:

    Acceleration = vect(0,0,0);

    DesiredRotation.Pitch = 0;
    if (!bSitting && !bDancing)
        PlayWaiting();

    // we are now idle
}


// ----------------------------------------------------------------------
// state FirstPersonConversation
//
// Just sit here until the conversation is over
// ----------------------------------------------------------------------

state FirstPersonConversation
{
    function Tick(float deltaTime)
    {
        Global.Tick(deltaTime);

        LipSynch(deltaTime);

        // Keep turning towards the person we're speaking to
        if (ConversationActor != None)
        {
            if (bSitting)
            {
                if (SeatActor != None)
                    LookAtActor(ConversationActor, true, true, true, 0, 1.0, SeatActor.Rotation.Yaw+49152, 5461);
                else
                    LookAtActor(ConversationActor, false, true, true, 0, 1.0);
            }
            else
                LookAtActor(ConversationActor, true, true, true, 0, 1.0);
        }
    }

    function LoopHeadConvoAnim()
    {
    }

    function SetOrders(Name orderName, optional Name newOrderTag, optional bool bImmediate)
    {
        ConvOrders   = orderName;
        ConvOrderTag = newOrderTag;
    }

    function FollowOrders(optional bool bDefer)
    {
        local name tempConvOrders, tempConvOrderTag;

        // hack
        tempConvOrders   = ConvOrders;
        tempConvOrderTag = ConvOrderTag;
        ResetConvOrders();  // must do this before calling SetOrders(), or recursion will result

        if (tempConvOrders != '')
            Global.SetOrders(tempConvOrders, tempConvOrderTag, true);
        else
            Global.FollowOrders(bDefer);
    }

    function BeginState()
    {
        local DeusExPlayer dxPlayer;
        local bool         bBlock;

        ResetConvOrders();
        EnableCheckDestLoc(false);

        dxPlayer = DeusExPlayer(GetPlayerPawn());
        /*
        bBlock = True;
        if (dxPlayer != None)
            if (dxPlayer.ConPlay != None)
                if (dxPlayer.ConPlay.CanInterrupt())
                    bBlock = False;
        */

        // 1st-person conversations will no longer block;
        // left old code in here in case people change their minds :)

        bBlock = false;

        bInterruptState = True;
        if (bBlock)
        {
            bCanConverse = False;
            MakePawnIgnored(true);
            BlockReactions(true);
        }
        else
        {
            bCanConverse = True;
            MakePawnIgnored(false);
            if ((dxPlayer != None) && (dxPlayer.conPlay != None) &&
                dxPlayer.conPlay.con.IsSpeakingActor(dxPlayer))
                SetReactions(false, false, false, false, true, false, false, true, false, false, true, true);
            else
                ResetReactions();
        }

        bConversationEndedNormally = False;
        bInConversation = True;
        bStasis = False;
        SetDistress(false);
        SeekPawn = None;
    }

    function EndState()
    {
        local DeusExPlayer player;

        bConvEndState = true;
        if (bConversationEndedNormally != True)
        {
            player = DeusExPlayer(GetPlayerPawn());
            player.AbortConversation();
        }
        bConvEndState = false;
        ResetConvOrders();

        StopBlendAnims();
        bInterruptState = true;
        bCanConverse    = True;
        MakePawnIgnored(false);
        ResetReactions();
        bStasis = True;
        ConversationActor = None;
    }

Begin:

    Acceleration = vect(0,0,0);

    DesiredRotation.Pitch = 0;
    if (!bSitting && !bDancing)
        PlayWaiting();

    // we are now idle
}


// ----------------------------------------------------------------------
// EnterConversationState()
// ----------------------------------------------------------------------

function EnterConversationState(bool bFirstPerson, optional bool bAvoidState)
{
    // First check to see if we're already in a conversation state, 
    // in which case we'll abort immediately

    if ((GetStateName() == 'Conversation') || (GetStateName() == 'FirstPersonConversation'))
        return;

    SetNextState(GetStateName(), 'Begin');

    // If bAvoidState is set, then we don't want to jump into the conversaton state
    // for the ScriptedPawn, because bad things might happen otherwise.

    if (!bAvoidState)
    {
        if (bFirstPerson)
            GotoState('FirstPersonConversation');
        else
            GotoState('Conversation');
    }
}


// ----------------------------------------------------------------------
// state Standing
//
// Just kinda stand there and do nothing.
// (similar to Wandering, except the pawn doesn't actually move)
// ----------------------------------------------------------------------

state Standing
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('Standing', 'ContinueStand');
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Tick(float deltaSeconds)
    {
        animTimer[1] += deltaSeconds;
        Global.Tick(deltaSeconds);
    }

    function BeginState()
    {
        StandUp();
        SetEnemy(None, EnemyLastSeen, true);
        Disable('AnimEnd');
        bCanJump = false;

        bStasis = False;

        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = True;

        if (JumpZ > 0)
            bCanJump = true;
        bStasis = True;

        StopBlendAnims();
    }

Begin:
    WaitForLanding();
    if (!bUseHome)
        Goto('StartStand');

MoveToBase:
    if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius))
    {
        EnableCheckDestLoc(true);
        while (true)
        {
            if (PointReachable(HomeLoc))
            {
                if (ShouldPlayWalk(HomeLoc))
                    PlayWalking();
                MoveTo(HomeLoc, GetWalkingSpeed());
                CheckDestLoc(HomeLoc);
                break;
            }
            else
            {
                MoveTarget = FindPathTo(HomeLoc);
                if (MoveTarget != None)
                {
                    if (ShouldPlayWalk(MoveTarget.Location))
                        PlayWalking();
                    MoveToward(MoveTarget, GetWalkingSpeed());
                    CheckDestLoc(MoveTarget.Location, true);
                }
                else
                    break;
            }
        }
        EnableCheckDestLoc(false);
    }
    TurnTo(Location+HomeRot);

StartStand:
    Acceleration=vect(0,0,0);
    Goto('Stand');

ContinueFromDoor:
    Goto('MoveToBase');

Stand:
ContinueStand:
    // nil
    bStasis = True;

    PlayWaiting();
    if (!bPlayIdle)
        Goto('DoNothing');
    Sleep(FRand()*14+8);

Fidget:
    if (FRand() < 0.5)
    {
        PlayIdle();
        FinishAnim();
    }
    else
    {
        if (FRand() > 0.5)
        {
            PlayTurnHead(LOOK_Up, 1.0, 1.0);
            Sleep(2.0);
            PlayTurnHead(LOOK_Forward, 1.0, 1.0);
            Sleep(0.5);
        }
        else if (FRand() > 0.5)
        {
            PlayTurnHead(LOOK_Left, 1.0, 1.0);
            Sleep(1.5);
            PlayTurnHead(LOOK_Forward, 1.0, 1.0);
            Sleep(0.9);
            PlayTurnHead(LOOK_Right, 1.0, 1.0);
            Sleep(1.2);
            PlayTurnHead(LOOK_Forward, 1.0, 1.0);
            Sleep(0.5);
        }
        else
        {
            PlayTurnHead(LOOK_Right, 1.0, 1.0);
            Sleep(1.5);
            PlayTurnHead(LOOK_Forward, 1.0, 1.0);
            Sleep(0.9);
            PlayTurnHead(LOOK_Left, 1.0, 1.0);
            Sleep(1.2);
            PlayTurnHead(LOOK_Forward, 1.0, 1.0);
            Sleep(0.5);
        }
    }
    if (FRand() < 0.3)
        PlayIdleSound();
    Goto('Stand');

DoNothing:
    // nil
}


// ----------------------------------------------------------------------
// state Dancing
//
// Dance in place.
// (Most of this code was ripped from Standing)
// ----------------------------------------------------------------------

state Dancing
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('Dancing', 'ContinueDance');
    }

    function AnimEnd()
    {
        PlayDancing();
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function BeginState()
    {
        if (bSitting && !bDancing)
            StandUp();
        SetEnemy(None, EnemyLastSeen, true);
        Disable('AnimEnd');
        bCanJump = false;

        bStasis = False;

        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = True;

        if (JumpZ > 0)
            bCanJump = true;
        bStasis = True;

        StopBlendAnims();
    }

Begin:
    WaitForLanding();
    if (bDancing)
    {
        if (bUseHome)
            TurnTo(Location+HomeRot);
        Goto('StartDance');
    }
    if (!bUseHome)
        Goto('StartDance');

MoveToBase:
    if (!IsPointInCylinder(self, HomeLoc, 16-CollisionRadius))
    {
        EnableCheckDestLoc(true);
        while (true)
        {
            if (PointReachable(HomeLoc))
            {
                if (ShouldPlayWalk(HomeLoc))
                    PlayWalking();
                MoveTo(HomeLoc, GetWalkingSpeed());
                CheckDestLoc(HomeLoc);
                break;
            }
            else
            {
                MoveTarget = FindPathTo(HomeLoc);
                if (MoveTarget != None)
                {
                    if (ShouldPlayWalk(MoveTarget.Location))
                        PlayWalking();
                    MoveToward(MoveTarget, GetWalkingSpeed());
                    CheckDestLoc(MoveTarget.Location, true);
                }
                else
                    break;
            }
        }
        EnableCheckDestLoc(false);
    }
    TurnTo(Location+HomeRot);

StartDance:
    Acceleration=vect(0,0,0);
    Goto('Dance');

ContinueFromDoor:
    Goto('MoveToBase');

Dance:
ContinueDance:
    // nil
    bDancing = True;
    PlayDancing();
    bStasis = True;
    if (!bHokeyPokey)
        Goto('DoNothing');

Spin:
    Sleep(FRand()*5+5);
    useRot = DesiredRotation;
    if (FRand() > 0.5)
    {
        TurnTo(Location+1000*vector(useRot+rot(0,16384,0)));
        TurnTo(Location+1000*vector(useRot+rot(0,32768,0)));
        TurnTo(Location+1000*vector(useRot+rot(0,49152,0)));
    }
    else
    {
        TurnTo(Location+1000*vector(useRot+rot(0,49152,0)));
        TurnTo(Location+1000*vector(useRot+rot(0,32768,0)));
        TurnTo(Location+1000*vector(useRot+rot(0,16384,0)));
    }
    TurnTo(Location+1000*vector(useRot));
    Goto('Spin');

DoNothing:
    // nil
}


// ----------------------------------------------------------------------
// state Sitting
//
// Just kinda sit there and do nothing.
// (doubles as a shitting state)
// ----------------------------------------------------------------------

state Sitting
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('Sitting', 'ContinueSit');
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        if (!bAcceptBump)
            NextDirection = TURNING_None;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function bool HandleTurn(Actor Other)
    {
        if (Other == SeatActor)
            return true;
        else
            return Global.HandleTurn(Other);
    }

    function Bump(actor bumper)
    {
        // If we hit our chair, move to the right place
        if ((bumper == SeatActor) && bAcceptBump)
        {
            bAcceptBump = false;
            GotoState('Sitting', 'CircleToFront');
        }

        // Handle conversations, if need be
        else
            Global.Bump(bumper);
    }

    function Tick(float deltaSeconds)
    {
        local vector endPos;
        local vector newPos;
        local float  delta;

        Global.Tick(deltaSeconds);

        if (bSitInterpolation && (SeatActor != None))
        {
            endPos = SitPosition(SeatActor, SeatSlot);
            if ((deltaSeconds < remainingSitTime) && (remainingSitTime > 0))
            {
                delta = deltaSeconds/remainingSitTime;
                newPos = (endPos-Location)*delta + Location;
                remainingSitTime -= deltaSeconds;
            }
            else
            {
                remainingSitTime = 0;
                bSitInterpolation = false;
                newPos = endPos;
                Acceleration = vect(0,0,0);
                Velocity = vect(0,0,0);
                SetBase(SeatActor);
                bSitting = true;
            }
            SetLocation(newPos);
            DesiredRotation = SeatActor.Rotation+Rot(0, -16384, 0);
        }
    }

    function Vector SitPosition(Seat seatActor, int slot)
    {
        local float newAssHeight;

        newAssHeight = GetDefaultCollisionHeight() + BaseAssHeight;
        newAssHeight = -(CollisionHeight - newAssHeight);

        return ((seatActor.sitPoint[slot]>>seatActor.Rotation)+seatActor.Location+(vect(0,0,-1)*newAssHeight));
    }

    function vector GetDestinationPosition(Seat seatActor, optional float extraDist)
    {
        local Rotator seatRot;
        local Vector  destPos;

        if (seatActor == None)
            return (Location);

        seatRot = seatActor.Rotation + Rot(0, -16384, 0);
        seatRot.Pitch = 0;
        destPos = seatActor.Location;
        destPos += vect(0,0,1)*(CollisionHeight-seatActor.CollisionHeight);
        destPos += Vector(seatRot)*(seatActor.CollisionRadius+CollisionRadius+extraDist);

        return (destPos);
    }

    function bool IsIntersectingSeat()
    {
        local bool   bIntersect;
        local vector testVector;

        bIntersect = false;
        if (SeatActor != None)
            bIntersect = IsOverlapping(SeatActor);

        return (bIntersect);
    }

    function int FindBestSlot(Seat seatActor, out float slotDist)
    {
        local int   bestSlot;
        local float dist;
        local float bestDist;
        local int   i;

        bestSlot = -1;
        bestDist = 100;
        if (!seatActor.Region.Zone.bWaterZone)
        {
            for (i=0; i<seatActor.numSitPoints; i++)
            {
                if (seatActor.sittingActor[i] == None)
                {
                    dist = VSize(SitPosition(seatActor, i) - Location);
                    if ((bestSlot < 0) || (bestDist > dist))
                    {
                        bestDist = dist;
                        bestSlot = i;
                    }
                }
            }
        }

        slotDist = bestDist;

        return (bestSlot);
    }

    function FindBestSeat()
    {
        local Seat  curSeat;
        local Seat  bestSeat;
        local float curDist;
        local float bestDist;
        local int   curSlot;
        local int   bestSlot;
        local bool  bTry;

        if (bUseFirstSeatOnly && bSeatHackUsed)
        {
            bestSeat = SeatHack;  // use the seat hack
            bestSlot = -1;
            if (!IsSeatValid(bestSeat))
                bestSeat = None;
            else
            {
                if (GetNextWaypoint(bestSeat) == None)
                    bestSeat = None;
                else
                {
                    bestSlot = FindBestSlot(bestSeat, curDist);
                    if (bestSlot < 0)
                        bestSeat = None;
                }
            }
        }
        else
        {
            bestSeat = Seat(OrderActor);  // try the ordered seat first
            if (bestSeat != None)
            {
                if (!IsSeatValid(OrderActor))
                    bestSeat = None;
                else
                {
                    if (GetNextWaypoint(bestSeat) == None)
                        bestSeat = None;
                    else
                    {
                        bestSlot = FindBestSlot(bestSeat, curDist);
                        if (bestSlot < 0)
                            bestSeat = None;
                    }
                }
            }
            if (bestSeat == None)
            {
                bestDist = 10001;
                bestSlot = -1;
                foreach RadiusActors(Class'Seat', curSeat, 10000)
                {
                    if (IsSeatValid(curSeat))
                    {
                        curSlot = FindBestSlot(curSeat, curDist);
                        if (curSlot >= 0)
                        {
                            if (bestDist > curDist)
                            {
                                if (GetNextWaypoint(curSeat) != None)
                                {
                                    bestDist = curDist;
                                    bestSeat = curSeat;
                                    bestSlot = curSlot;
                                }
                            }
                        }
                    }
                }
            }
        }

        if (bestSeat != None)
        {
            bestSeat.sittingActor[bestSlot] = self;
            SeatLocation       = bestSeat.Location;
            bSeatLocationValid = true;
        }
        else
            bSeatLocationValid = false;

        if (bUseFirstSeatOnly && !bSeatHackUsed)
        {
            SeatHack      = bestSeat;
            bSeatHackUsed = true;
        }

        SeatActor = bestSeat;
        SeatSlot  = bestSlot;
    }

    function FollowSeatFallbackOrders()
    {
        FindBestSeat();
        if (IsSeatValid(SeatActor))
            GotoState('Sitting', 'Begin');
        else
            GotoState('Wandering');
    }

    function BeginState()
    {
        SetEnemy(None, EnemyLastSeen, true);
        Disable('AnimEnd');
        bCanJump = false;

        bAcceptBump = True;

        if (SeatActor == None)
            FindBestSeat();

        bSitInterpolation = false;

        bStasis = False;

        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(true);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        if (!bSitting)
            StandUp();

        bAcceptBump = True;

        if (JumpZ > 0)
            bCanJump = true;

        bSitInterpolation = false;

        bStasis = True;
    }

Begin:
    WaitForLanding();
    if (!IsSeatValid(SeatActor))
        FollowSeatFallbackOrders();
    if (!bSitting)
        WaitForLanding();
    else
    {
        TurnTo(Vector(SeatActor.Rotation+Rot(0, -16384, 0))*100+Location);
        Goto('ContinueSitting');
    }

MoveToSeat:
    if (IsIntersectingSeat())
        Goto('MoveToPosition');
    bAcceptBump = true;
    while (true)
    {
        if (!IsSeatValid(SeatActor))
            FollowSeatFallbackOrders();
        destLoc = GetDestinationPosition(SeatActor);
        if (PointReachable(destLoc))
        {
            if (ShouldPlayWalk(destLoc))
                PlayWalking();
            MoveTo(destLoc, GetWalkingSpeed());
            CheckDestLoc(destLoc);

            if (IsPointInCylinder(self, GetDestinationPosition(SeatActor), 16, 16))
            {
                bAcceptBump = false;
                Goto('MoveToPosition');
                break;
            }
        }
        else
        {
            MoveTarget = GetNextWaypoint(SeatActor);
            if (MoveTarget != None)
            {
                if (ShouldPlayWalk(MoveTarget.Location))
                    PlayWalking();
                MoveToward(MoveTarget, GetWalkingSpeed());
                CheckDestLoc(MoveTarget.Location, true);
            }
            else
                break;
        }
    }

CircleToFront:
    bAcceptBump = false;
    if (!IsSeatValid(SeatActor))
        FollowSeatFallbackOrders();
    if (ShouldPlayWalk(GetDestinationPosition(SeatActor, 16)))
        PlayWalking();
    MoveTo(GetDestinationPosition(SeatActor, 16), GetWalkingSpeed());

MoveToPosition:
    if (!IsSeatValid(SeatActor))
        FollowSeatFallbackOrders();
    bSitting = true;
    EnableCollision(false);
    Acceleration=vect(0,0,0);

Sit:
    Acceleration=vect(0,0,0);
    Velocity=vect(0,0,0);
    if (!IsSeatValid(SeatActor))
        FollowSeatFallbackOrders();
    remainingSitTime = 0.8;
    PlaySittingDown();
    SetBasedPawnSize(CollisionRadius, GetSitHeight());
    SetPhysics(PHYS_Flying);
    StopStanding();
    bSitInterpolation = true;
    while (bSitInterpolation)
        Sleep(0);
    FinishAnim();
    Goto('ContinueSitting');

ContinueFromDoor:
    Goto('MoveToSeat');

ContinueSitting:
    if (!IsSeatValid(SeatActor))
        FollowSeatFallbackOrders();
    SetBasedPawnSize(CollisionRadius, GetSitHeight());
    SetCollision(Default.bCollideActors, Default.bBlockActors, Default.bBlockPlayers);
    PlaySitting();
    bStasis  = True;
    // nil

}


// ----------------------------------------------------------------------
// state HandlingEnemy
//
// Fight-or-flight state
// ----------------------------------------------------------------------

state HandlingEnemy
{
    function BeginState()
    {
        if (Enemy == None)
            GotoState('Seeking');
        else if (RaiseAlarm == RAISEALARM_BeforeAttacking)
            GotoState('Alerting');
        else
            GotoState('Attacking');
    }

Begin:

}

// ----------------------------------------------------------------------
// state Wandering
//
// Meander from place to place, occasionally gazing into the middle
// distance.
// ----------------------------------------------------------------------

state Wandering
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('Wandering', 'ContinueWander');
    }

    function Bump(actor bumper)
    {
        if (bAcceptBump)
        {
            // If we get bumped by another actor while we wait, start wandering again
            bAcceptBump = False;
            Disable('AnimEnd');
            GotoState('Wandering', 'Wander');
        }

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function bool GoHome()
    {
        if (bUseHome && !IsNearHome(Location))
        {
            destLoc   = HomeLoc;
            destPoint = None;
            if (PointReachable(destLoc))
                return true;
            else
            {
                MoveTarget = FindPathTo(destLoc);
                if (MoveTarget != None)
                    return true;
                else
                    return false;
            }
        }
        else
            return false;
    }

    function PickDestination()
    {
        local WanderCandidates candidates[5];
        local int              candidateCount;
        local int              maxCandidates;
        local int              maxLastPoints;

        local WanderPoint curPoint;
        local Actor       wayPoint;
        local int         i;
        local int         openSlot;
        local float       maxDist;
        local float       dist;
        local float       angle;
        local float       magnitude;
        local int         iterations;
        local bool        bSuccess;
        local Rotator     rot;

        maxCandidates = 4;  // must be <= size of candidates[] array
        maxLastPoints = 2;  // must be <= size of lastPoints[] array

        for (i=0; i<maxCandidates; i++)
            candidates[i].dist = 100000;
        candidateCount = 0;

        // A certain percentage of the time, we want to angle off to a random direction...
        if ((RandomWandering < 1) && (FRand() > RandomWandering))
        {
            // Fill the candidate table
            foreach RadiusActors(Class'WanderPoint', curPoint, 3000*wanderlust+1000)  // 1000-4000
            {
                // Make sure we haven't been here recently
                for (i=0; i<maxLastPoints; i++)
                {
                    if (lastPoints[i] == curPoint)
                        break;
                }

                if (i >= maxLastPoints)
                {
                    // Can we get there from here?
                    wayPoint = GetNextWaypoint(curPoint);

                    if ((wayPoint != None) && !IsNearHome(curPoint.Location))
                        wayPoint = None;

                    // Yep
                    if (wayPoint != None)
                    {
                        // Find an empty slot for this candidate
                        openSlot = -1;
                        dist     = VSize(curPoint.location - location);
                        maxDist  = dist;

                        // This candidate will only replace more distant candidates...
                        for (i=0; i<maxCandidates; i++)
                        {
                            if (maxDist < candidates[i].dist)
                            {
                                maxDist  = candidates[i].dist;
                                openSlot = i;
                            }
                        }

                        // Put the candidate in the (unsorted) list
                        if (openSlot >= 0)
                        {
                            candidates[openSlot].point    = curPoint;
                            candidates[openSlot].waypoint = wayPoint;
                            candidates[openSlot].dist     = dist;
                            if (candidateCount < maxCandidates)
                                candidateCount++;
                        }
                    }
                }
            }
        }

        // Shift our list of recently visited points
        for (i=maxLastPoints-1; i>0; i--)
            lastPoints[i] = lastPoints[i-1];
        lastPoints[0] = None;

        // Do we have a list of candidates?
        if (candidateCount > 0)
        {
            // Pick a candidate at random
            i = Rand(candidateCount);
            curPoint = candidates[i].point;
            wayPoint = candidates[i].waypoint;
            lastPoints[0] = curPoint;
            MoveTarget    = wayPoint;
            destPoint     = curPoint;
        }

        // No candidates -- find a random place to go
        else
        {
            MoveTarget = None;
            destPoint  = None;
            iterations = 6;  // try up to 6 different directions
            while (iterations > 0)
            {
                // How far will we go?
                magnitude = (wanderlust*400+200) * (FRand()*0.2+0.9); // 200-600, +/-10%

                // Choose our destination, based on whether we have a home base
                if (!bUseHome)
                    bSuccess = AIPickRandomDestination(100, magnitude, 0, 0, 0, 0, 1,
                                                       FRand()*0.4+0.35, destLoc);
                else
                {
                    if (magnitude > HomeExtent)
                        magnitude = HomeExtent*(FRand()*0.2+0.9);
                    rot = Rotator(HomeLoc-Location);
                    bSuccess = AIPickRandomDestination(50, magnitude, rot.Yaw, 0.25, rot.Pitch, 0.25, 1,
                                                       FRand()*0.4+0.35, destLoc);
                }

                // Success?  Break out of the iteration loop
                if (bSuccess)
                    if (IsNearHome(destLoc))
                        break;

                // We failed -- try again
                iterations--;
            }

            // If we got a destination, go there
            if (iterations <= 0)
                destLoc = Location;
        }
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function BeginState()
    {
        StandUp();
        SetEnemy(None, EnemyLastSeen, true);
        Disable('AnimEnd');
        bCanJump = false;
        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        local int i;
        bAcceptBump = True;

        EnableCheckDestLoc(false);

        // Clear out our list of last visited points
        for (i=0; i<ArrayCount(lastPoints); i++)
            lastPoints[i] = None;

        if (JumpZ > 0)
            bCanJump = true;
    }

Begin:
    destPoint = None;

GoHome:
    bAcceptBump = false;
    WaitForLanding();
    if (!GoHome())
        Goto('WanderInternal');

MoveHome:
    EnableCheckDestLoc(true);
    while (true)
    {
        if (PointReachable(destLoc))
        {
            if (ShouldPlayWalk(destLoc))
                PlayWalking();
            MoveTo(destLoc, GetWalkingSpeed());
            CheckDestLoc(destLoc);
            break;
        }
        else
        {
            MoveTarget = FindPathTo(destLoc);
            if (MoveTarget != None)
            {
                if (ShouldPlayWalk(MoveTarget.Location))
                    PlayWalking();
                MoveToward(MoveTarget, GetWalkingSpeed());
                CheckDestLoc(MoveTarget.Location, true);
            }
            else
                break;
        }
    }
    EnableCheckDestLoc(false);
    Goto('Pausing');

Wander:
    WaitForLanding();
WanderInternal:
    PickDestination();

Moving:
    // Move from pathnode to pathnode until we get where we're going
    // (ooooold code -- no longer used)
    if (destPoint != None)
    {
        if (ShouldPlayWalk(MoveTarget.Location))
            PlayWalking();
        MoveToward(MoveTarget, GetWalkingSpeed());
        while ((MoveTarget != None) && (MoveTarget != destPoint))
        {
            MoveTarget = FindPathToward(destPoint);
            if (MoveTarget != None)
            {
                if (ShouldPlayWalk(MoveTarget.Location))
                    PlayWalking();
                MoveToward(MoveTarget, GetWalkingSpeed());
            }
        }
    }
    else if (destLoc != Location)
    {
        if (ShouldPlayWalk(destLoc))
            PlayWalking();
        MoveTo(destLoc, GetWalkingSpeed());
    }
    else
        Sleep(0.5);

Pausing:
    Acceleration = vect(0, 0, 0);

    // Turn in the direction dictated by the WanderPoint, if there is one
    sleepTime = 6.0;
    if (WanderPoint(destPoint) != None)
    {
        if (WanderPoint(destPoint).gazeItem != None)
        {
            TurnToward(WanderPoint(destPoint).gazeItem);
            sleepTime = WanderPoint(destPoint).gazeDuration;
        }
        else if (WanderPoint(destPoint).gazeDirection != vect(0, 0, 0))
            TurnTo(Location + WanderPoint(destPoint).gazeDirection);
    }
    Enable('AnimEnd');
    TweenToWaiting(0.2);
    bAcceptBump = True;
    PlayScanningSound();
    sleepTime *= (-0.9*restlessness) + 1;
    Sleep(sleepTime);
    Disable('AnimEnd');
    bAcceptBump = False;
    FinishAnim();
    Goto('Wander');

ContinueWander:
ContinueFromDoor:
    FinishAnim();
    PlayWalking();
    Goto('Wander');
}


// ----------------------------------------------------------------------
// state Leaving
//
// Just like Patrolling, but make the pawn transient.
// ----------------------------------------------------------------------

State Leaving
{
    function BeginState()
    {
        bTransient = True;  // this pawn will be destroyed when it gets out of range
        bDisappear = True;
        GotoState('Patrolling');
    }

Begin:
    // shouldn't ever reach this point
}


// ----------------------------------------------------------------------
// state Patrolling
//
// Move from point to point in a predescribed pattern.
// ----------------------------------------------------------------------

State Patrolling
{
    function SetFall()
    {
        StartFalling('Patrolling', 'ContinuePatrol');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }
    
    function AnimEnd()
    {
        PlayWaiting();
    }

    function PatrolPoint PickStartPoint()
    {
        local NavigationPoint nav;
        local PatrolPoint     curNav;
        local float           curDist;
        local PatrolPoint     closestNav;
        local float           closestDist;

        nav = Level.NavigationPointList;
        while (nav != None)
        {
            nav.visitedWeight = 0;
            nav = nav.nextNavigationPoint;
        }

        closestNav  = None;
        closestDist = 100000;
        nav = Level.NavigationPointList;
        while (nav != None)
        {
            curNav = PatrolPoint(nav);
            if ((curNav != None) && (curNav.Tag == OrderTag))
            {
                while (curNav != None)
                {
                    if (curNav.visitedWeight != 0)  // been here before
                        break;
                    curDist = VSize(Location - curNav.Location);
                    if ((closestNav == None) || (closestDist > curDist))
                    {
                        closestNav  = curNav;
                        closestDist = curDist;
                    }
                    curNav.visitedWeight = 1;
                    curNav = curNav.NextPatrolPoint;
                }
            }
            nav = nav.nextNavigationPoint;
        }

        return (closestNav);
    }

    function PickDestination()
    {
        if (PatrolPoint(destPoint) != None)
            destPoint = PatrolPoint(destPoint).NextPatrolPoint;
        else
            destPoint = PickStartPoint();
        if (destPoint == None)  // can't go anywhere...
            GotoState('Standing');
    }

    function BeginState()
    {
        StandUp();
        SetEnemy(None, EnemyLastSeen, true);
        Disable('AnimEnd');
        SetupWeapon(false);
        SetDistress(false);
        bStasis = false;
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        Enable('AnimEnd');
        bStasis = true;
    }

Begin:
    destPoint = None;

Patrol:
    //Disable('Bump');
    WaitForLanding();
    PickDestination();

Moving:
    // Move from pathnode to pathnode until we get where we're going
    if (destPoint != None)
    {
        if (!IsPointInCylinder(self, destPoint.Location, 16-CollisionRadius))
        {
            EnableCheckDestLoc(true);
            MoveTarget = FindPathToward(destPoint);
            while (MoveTarget != None)
            {
                if (ShouldPlayWalk(MoveTarget.Location))
                    PlayWalking();
                MoveToward(MoveTarget, GetWalkingSpeed());
                CheckDestLoc(MoveTarget.Location, true);
                if (MoveTarget == destPoint)
                    break;
                MoveTarget = FindPathToward(destPoint);
            }
            EnableCheckDestLoc(false);
        }
    }
    else
        Goto('Patrol');

Pausing:
    if (!bAlwaysPatrol)
        bStasis = true;
    Acceleration = vect(0, 0, 0);

    // Turn in the direction dictated by the WanderPoint, or a random direction
    if (PatrolPoint(destPoint) != None)
    {
        if ((PatrolPoint(destPoint).pausetime > 0) || (PatrolPoint(destPoint).NextPatrolPoint == None))
        {
            if (ShouldPlayTurn(Location + PatrolPoint(destPoint).lookdir))
                PlayTurning();
            TurnTo(Location + PatrolPoint(destPoint).lookdir);
            Enable('AnimEnd');
            TweenToWaiting(0.2);
            PlayScanningSound();
            //Enable('Bump');
            sleepTime = PatrolPoint(destPoint).pausetime * ((-0.9*restlessness) + 1);
            Sleep(sleepTime);
            Disable('AnimEnd');
            //Disable('Bump');
            FinishAnim();
        }
    }
    Goto('Patrol');

ContinuePatrol:
ContinueFromDoor:
    FinishAnim();
    PlayWalking();
    Goto('Moving');

}


// ----------------------------------------------------------------------
// state Seeking
//
// Look for enemies in the area
// ----------------------------------------------------------------------

State Seeking
{
    function SetFall()
    {
        StartFalling('Seeking', 'ContinueSeek');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function bool GetNextLocation(out vector nextLoc)
    {
        local float   dist;
        local rotator rotation;
        local bool    bDone;
        local float   seekDistance;
        local Actor   hitActor;
        local vector  HitLocation, HitNormal;
        local vector  diffVect;
        local bool    bLOS;

        if (bSeekLocation)
        {
            if (SeekType == SEEKTYPE_Guess)
                seekDistance = (200+FClamp(GroundSpeed*EnemyLastSeen*0.5, 0, 1000));
            else
                seekDistance = 300;
        }
        else
            seekDistance = 60;

        dist  = VSize(Location-destLoc);
        bDone = false;
        bLOS  = false;

        if (dist < seekDistance)
        {
            bLOS = true;
            foreach TraceVisibleActors(Class'Actor', hitActor, hitLocation, hitNormal,
                                       destLoc, Location+vect(0,0,1)*BaseEyeHeight)
            {
                if (hitActor != self)
                {
                    if (hitActor == Level)
                        bLOS = false;
                    else if (IsPointInCylinder(hitActor, destLoc, 16, 16))
                        break;
                    else if (hitActor.bBlockSight && !hitActor.bHidden)
                        bLOS = false;
                }
                if (!bLOS)
                    break;
            }
        }

        if (!bLOS)
        {
            if (PointReachable(destLoc))
            {
                rotation = Rotator(destLoc - Location);
                if (seekDistance == 0)
                    nextLoc = destLoc;
                else if (!AIDirectionReachable(destLoc, rotation.Yaw, rotation.Pitch, 0, seekDistance, nextLoc))
                    bDone = true;
                if (!bDone && bDefendHome && !IsNearHome(nextLoc))
                    bDone = true;
                if (!bDone)  // hack, because Unreal's movement code SUCKS
                {
                    diffVect = nextLoc - Location;
                    if (Physics == PHYS_Walking)
                        diffVect *= vect(1,1,0);
                    if (VSize(diffVect) < 20)
                        bDone = true;
                    else if (IsPointInCylinder(self, nextLoc, 10, 10))
                        bDone = true;
                }
            }
            else
            {
                MoveTarget = FindPathTo(destLoc);
                if (MoveTarget == None)
                    bDone = true;
                else if (bDefendHome && !IsNearHome(MoveTarget.Location))
                    bDone = true;
                else
                    nextLoc = MoveTarget.Location;
            }
        }
        else
            bDone = true;

        return (!bDone);
    }

    function bool PickDestination()
    {
        local bool bValid;

        bValid = false;
        if (/*(EnemyLastSeen <= 25.0) &&*/ (SeekLevel > 0))
        {
            if (bSeekLocation)
            {
                bValid  = true;
                destLoc = LastSeenPos;
            }
            else
            {
                bValid = AIPickRandomDestination(130, 250, 0, 0, 0, 0, 2, 1.0, destLoc);
                if (!bValid)
                {
                    bValid  = true;
                    destLoc = Location + VRand()*50;
                }
                else
                    destLoc += vect(0,0,1)*BaseEyeHeight;
            }
        }

        return (bValid);
    }

    function NavigationPoint GetOvershootDestination(float randomness, optional float focus)
    {
        local NavigationPoint navPoint, bestPoint;
        local float           distance;
        local float           score, bestScore;
        local int             yaw;
        local rotator         rot;
        local float           yawCutoff;

        if (focus <= 0)
            focus = 0.6;

        yawCutoff = int(32768*focus);
        bestPoint = None;
        bestScore = 0;

        foreach ReachablePathnodes(Class'NavigationPoint', navPoint, None, distance)
        {
            if (distance < 1)
                distance = 1;
            rot = Rotator(navPoint.Location-Location);
            yaw = rot.Yaw + (16384*randomness);
            yaw = (yaw-Rotation.Yaw) & 0xFFFF;
            if (yaw > 32767)
                yaw  -= 65536;
            yaw = abs(yaw);
            if (yaw <= yawCutoff)
            {
                score = yaw/distance;
                if ((bestPoint == None) || (score < bestScore))
                {
                    bestPoint = navPoint;
                    bestScore = score;
                }
            }
        }

        return bestPoint;
    }

    function Tick(float deltaSeconds)
    {
        animTimer[1] += deltaSeconds;
        Global.Tick(deltaSeconds);
        UpdateActorVisibility(Enemy, deltaSeconds, 1.0, true);
    }

    function HandleLoudNoise(Name event, EAIEventState state, XAIParams params)
    {
        local Actor bestActor;
        local Pawn  instigator;

        if (state == EAISTATE_Begin || state == EAISTATE_Pulse)
        {
            bestActor = params.bestActor;
            if ((bestActor != None) && (EnemyLastSeen > 2.0))
            {
                instigator = Pawn(bestActor);
                if (instigator == None)
                    instigator = bestActor.Instigator;
                if (instigator != None)
                {
                    if (IsValidEnemy(instigator))
                    {
                        SetSeekLocation(instigator, bestActor.Location, SEEKTYPE_Sound);
                        destLoc = LastSeenPos;
                        if (bInterruptSeek)
                            GotoState('Seeking', 'GoToLocation');
                    }
                }
            }
        }
    }

    function HandleSighting(Pawn pawnSighted)
    {
        if ((EnemyLastSeen > 2.0) && IsValidEnemy(pawnSighted))
        {
            SetSeekLocation(pawnSighted, pawnSighted.Location, SEEKTYPE_Sight);
            destLoc = LastSeenPos;
            if (bInterruptSeek)
                GotoState('Seeking', 'GoToLocation');
        }
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        destLoc = LastSeenPos;
        SetReactions(true, true, false, true, true, true, true, true, true, false, true, true);
        bCanConverse = False;
        bStasis = False;
        SetupWeapon(true);
        SetDistress(false);
        bInterruptSeek = false;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        Enable('AnimEnd');
        ResetReactions();
        bCanConverse = True;
        bStasis = True;
        StopBlendAnims();
        SeekLevel = 0;
    }

Begin:
    WaitForLanding();
    PlayWaiting();
    if ((Weapon != None) && bKeepWeaponDrawn && (Weapon.CockingSound != None) && !bSeekPostCombat)
        PlaySound(Weapon.CockingSound, SLOT_None,,, 1024);
    Acceleration = vect(0,0,0);
    if (!PickDestination())
        Goto('DoneSeek');

GoToLocation:
    bInterruptSeek = true;
    Acceleration = vect(0,0,0);

    if ((DeusExWeapon(Weapon) != None) && DeusExWeapon(Weapon).CanReload() && !Weapon.IsInState('Reload'))
        DeusExWeapon(Weapon).ReloadAmmo();

    if (bSeekPostCombat)
        PlayPostAttackSearchingSound();
    else if (SeekType == SEEKTYPE_Sound)
        PlayPreAttackSearchingSound();
    else if (SeekType == SEEKTYPE_Sight)
    {
        if (ReactionLevel > 0.5)
            PlayPreAttackSightingSound();
    }
    else if ((SeekType == SEEKTYPE_Carcass) && bSeekLocation)
        PlayCarcassSound();

    StopBlendAnims();

    if ((SeekType == SEEKTYPE_Sight) && bSeekLocation)
        Goto('TurnToLocation');

    EnableCheckDestLoc(true);
    while (GetNextLocation(useLoc))
    {
        if (ShouldPlayWalk(useLoc))
            PlayRunning();
        MoveTo(useLoc, MaxDesiredSpeed);
        CheckDestLoc(useLoc);
    }
    EnableCheckDestLoc(false);

    if ((SeekType == SEEKTYPE_Guess) && bSeekLocation)
    {
        MoveTarget = GetOvershootDestination(0.5);
        if (MoveTarget != None)
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
        }

        if (AIPickRandomDestination(CollisionRadius*2, 200+FRand()*200, Rotation.Yaw, 0.75, Rotation.Pitch, 0.75, 2,
                                    0.4, useLoc))
        {
            if (ShouldPlayWalk(useLoc))
                PlayRunning();
            MoveTo(useLoc, MaxDesiredSpeed);
        }
    }

TurnToLocation:
    Acceleration = vect(0,0,0);
    PlayTurning();
    if ((SeekType == SEEKTYPE_Guess) && bSeekLocation)
        destLoc = Location + Vector(Rotation+(rot(0,1,0)*(Rand(16384)-8192)))*1000;
    if (bCanTurnHead)
    {
        Sleep(0);  // needed to turn head
        LookAtVector(destLoc, true, false, true);
        TurnTo(Vector(DesiredRotation)*1000+Location);
    }
    else
        TurnTo(destLoc);
    bSeekLocation = false;
    bInterruptSeek = false;

    PlayWaiting();
    Sleep(FRand()*1.5+3.0);

LookAround:
    if (bCanTurnHead)
    {
        if (FRand() < 0.5)
        {
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Left, 1.0, 1.0);
                Sleep(1.0);
            }
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Forward, 1.0, 1.0);
                Sleep(0.5);
            }
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Right, 1.0, 1.0);
                Sleep(1.0);
            }
        }
        else
        {
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Right, 1.0, 1.0);
                Sleep(1.0);
            }
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Forward, 1.0, 1.0);
                Sleep(0.5);
            }
            if (!bSeekLocation)
            {
                PlayTurnHead(LOOK_Left, 1.0, 1.0);
                Sleep(1.0);
            }
        }
        PlayTurnHead(LOOK_Forward, 1.0, 1.0);
        Sleep(0.5);
        StopBlendAnims();
    }
    else
    {
        if (!bSeekLocation)
            Sleep(1.0);
    }

FindAnotherPlace:
    SeekLevel--;
    if (PickDestination())
        Goto('GoToLocation');

DoneSeek:
    if (bSeekPostCombat)
        PlayTargetLostSound();
    else
        PlaySearchGiveUpSound();
    bSeekPostCombat = false;
    SeekPawn = None;
    if (Orders != 'Seeking')
        FollowOrders();
    else
        GotoState('Wandering');

ContinueSeek:
ContinueFromDoor:
    FinishAnim();
    Goto('FindAnotherPlace');

}


// ----------------------------------------------------------------------
// state Fleeing
//
// Run like a bat outta hell away from an actor.
// ----------------------------------------------------------------------

State Fleeing
{
    function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
    {
        local Name currentState;
        local Pawn oldEnemy;
        local name newLabel;
        local bool bHateThisInjury;
        local bool bFearThisInjury;
        local bool bAttack;

        if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury))
        {
            currentState = GetStateName();

            bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury);
            bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury);

            if (bHateThisInjury)
                IncreaseAgitation(instigatedBy);
            if (bFearThisInjury)
                IncreaseFear(instigatedBy, 2.0);

            oldEnemy = Enemy;

            bAttack = false;
            if (SetEnemy(instigatedBy))
            {
                if (!ShouldFlee())
                {
                    SwitchToBestWeapon();
                    if (Weapon != None)
                        bAttack = true;
                }
            }
            else
                SetEnemy(instigatedBy, , true);

            if (bAttack)
            {
                SetDistressTimer();
                SetNextState('HandlingEnemy');
            }
            else
            {
                SetDistressTimer();
                if (oldEnemy != Enemy)
                    newLabel = 'Begin';
                else
                    newLabel = 'ContinueFlee';
                SetNextState('Fleeing', newLabel);
            }
            GotoDisabledState(damageType, hitPos);
        }
    }

    function SetFall()
    {
        StartFalling('Fleeing', 'ContinueFlee');
    }

    function FinishFleeing()
    {
        if (bLeaveAfterFleeing)
            GotoState('Wandering');
        else
            FollowOrders();
    }

    function bool InSeat(out vector newLoc)  // hack
    {
        local Seat curSeat;
        local bool bSeat;

        bSeat = false;
        foreach RadiusActors(Class'Seat', curSeat, 200)
        {
            if (IsOverlapping(curSeat))
            {
                bSeat = true;
                newLoc = curSeat.Location + vector(curSeat.Rotation+Rot(0, -16384, 0))*(CollisionRadius+curSeat.CollisionRadius+20);
                break;
            }
        }

        return (bSeat);
    }

    function Tick(float deltaSeconds)
    {
        UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false);
        if (IsValidEnemy(Enemy))
        {
            if (EnemyLastSeen > FearSustainTime)
                FinishFleeing();
        }
        else if (!IsValidEnemy(Enemy, false))
            FinishFleeing();
        else if (!IsFearful())
            FinishFleeing();
        Global.Tick(deltaSeconds);
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }
    
    function AnimEnd()
    {
        PlayWaiting();
    }

    function PickDestination()
    {
        local HidePoint      hidePoint;
        local Actor          waypoint;
        local float          dist;
        local float          score;
        local Vector         vector1, vector2;
        local Rotator        rotator1;
        local float          tmpDist;

        local float          bestDist;
        local float          bestScore;

        local FleeCandidates candidates[5];
        local int            candidateCount;
        local int            maxCandidates;

        local float          maxDist;
        local int            openSlot;
        local float          maxScore;
        local int            i;
        local bool           bReplace;

        local float          angle;
        local float          magnitude;
        local int            iterations;

        local NearbyProjectileList projList;
        local bool                 bSuccess;

        maxCandidates  = 3;  // must be <= size of candidates[] arrays
        maxDist        = 10000;

        // Initialize the list of candidates
        for (i=0; i<maxCandidates; i++)
        {
            candidates[i].score = -1;
            candidates[i].dist  = maxDist+1;
        }
        candidateCount = 0;

        MoveTarget = None;
        destPoint  = None;

        if (bAvoidHarm)
        {
            GetProjectileList(projList, Location);
            if (IsLocationDangerous(projList, Location))
            {
                vector1 = ComputeAwayVector(projList);
                rotator1 = Rotator(vector1);
                if (AIDirectionReachable(Location, rotator1.Yaw, rotator1.Pitch, CollisionRadius+24, VSize(vector1), destLoc))
                    return;   // eck -- hack!!!
            }
        }

        if (Enemy != None)
        {
            foreach RadiusActors(Class'HidePoint', hidePoint, maxDist)
            {
                // Can the boogeyman see our hiding spot?
                if (!enemy.LineOfSightTo(hidePoint))
                {
                    // More importantly, can we REACH our hiding spot?
                    waypoint = GetNextWaypoint(hidePoint);
                    if (waypoint != None)
                    {
                        // How far is it to the hiding place?
                        dist = VSize(hidePoint.Location - Location);

                        // Determine vectors to the waypoint and our enemy
                        vector1 = enemy.Location - Location;
                        vector2 = waypoint.Location - Location;

                        // Strip out magnitudes from the vectors
                        tmpDist = VSize(vector1);
                        if (tmpDist > 0)
                            vector1 /= tmpDist;
                        tmpDist = VSize(vector2);
                        if (tmpDist > 0)
                            vector2 /= tmpDist;

                        // Add them
                        vector1 += vector2;

                        // Compute a score (a function of angle)
                        score = VSize(vector1);
                        score = 4-(score*score);

                        // Find an empty slot for this candidate
                        openSlot  = -1;
                        bestScore = score;
                        bestDist  = dist;

                        for (i=0; i<maxCandidates; i++)
                        {
                            // Can we replace the candidate in this slot?
                            if (bestScore > candidates[i].score)
                                bReplace = TRUE;
                            else if ((bestScore == candidates[i].score) &&
                                     (bestDist < candidates[i].dist))
                                bReplace = TRUE;
                            else
                                bReplace = FALSE;
                            if (bReplace)
                            {
                                bestScore = candidates[i].score;
                                bestDist  = candidates[i].dist;
                                openSlot = i;
                            }
                        }

                        // We found an open slot -- put our candidate here
                        if (openSlot >= 0)
                        {
                            candidates[openSlot].point    = hidePoint;
                            candidates[openSlot].waypoint = waypoint;
                            candidates[openSlot].location = waypoint.Location;
                            candidates[openSlot].score    = score;
                            candidates[openSlot].dist     = dist;
                            if (candidateCount < maxCandidates)
                                candidateCount++;
                        }
                    }
                }
            }

            // Any candidates?
            if (candidateCount > 0)
            {
                // Find a random candidate
                // (candidates moving AWAY from the enemy have a higher
                // probability of being chosen than candidates moving
                // TOWARDS the enemy)

                maxScore = 0;
                for (i=0; i<candidateCount; i++)
                    maxScore += candidates[i].score;
                score = FRand() * maxScore;
                for (i=0; i<candidateCount; i++)
                {
                    score -= candidates[i].score;
                    if (score <= 0)
                        break;
                }
                destPoint  = candidates[i].point;
                MoveTarget = candidates[i].waypoint;
                destLoc    = candidates[i].location;
            }
            else
            {
                iterations = 4;
                magnitude = 400*(FRand()*0.4+0.8);  // 400, +/-20%
                rotator1 = Rotator(Location-Enemy.Location);
                if (!AIPickRandomDestination(100, magnitude, rotator1.Yaw, 0.6, rotator1.Pitch, 0.6, iterations,
                                             FRand()*0.4+0.35, destLoc))
                    destLoc = Location+(VRand()*1200);  // we give up
            }
        }
        else
            destLoc = Location+(VRand()*1200);  // we give up
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        //Disable('Bump');
        BlockReactions();
        if (!bCower)
            bCanConverse = False;
        bStasis = False;
        SetupWeapon(false, true);
        SetDistress(true);
        EnemyReadiness = 1.0;
        //ReactionLevel  = 1.0;
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        Enable('AnimEnd');
        //Enable('Bump');
        ResetReactions();
        if (!bCower)
            bCanConverse = True;
        bStasis = True;
    }

Begin:
    //EnemyLastSeen = 0;
    destPoint = None;

Surprise:
    if ((1.0-ReactionLevel)*SurprisePeriod < 0.25)
        Goto('Flee');
    Acceleration=vect(0,0,0);
    PlaySurpriseSound();
    PlayWaiting();
    Sleep(FRand()*0.5);
    if (Enemy != None)
        TurnToward(Enemy);
    if (bCower)
        Goto('Flee');
    Sleep(FRand()*0.5+0.5);

Flee:
    if (bLeaveAfterFleeing)
    {
        bTransient = true;
        bDisappear = true;
    }
    if (bCower)
        Goto('Cower');
    WaitForLanding();
    PickDestination();

Moving:
    Sleep(0.0);

    if (enemy == None)
    {
        Acceleration = vect(0,0,0);
        PlayWaiting();
        Sleep(2.0);
        FinishFleeing();
    }

    // Move from pathnode to pathnode until we get where we're going
    if (destPoint != None)
    {
        EnableCheckDestLoc(true);
        while (MoveTarget != None)
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
            CheckDestLoc(MoveTarget.Location, true);
            if (enemy.bDetectable && enemy.AICanSee(destPoint, 1.0, false, false, false, true) > 0)
            {
                PickDestination();
                EnableCheckDestLoc(false);
                Goto('Moving');
            }
            if (MoveTarget == destPoint)
                break;
            MoveTarget = FindPathToward(destPoint);
        }
        EnableCheckDestLoc(false);
    }
    else if (PointReachable(destLoc))
    {
        if (ShouldPlayWalk(destLoc))
            PlayRunning();
        MoveTo(destLoc, MaxDesiredSpeed);
        if (enemy.bDetectable && enemy.AICanSee(Self, 1.0, false, false, true, true) > 0)
        {
            PickDestination();
            Goto('Moving');
        }
    }
    else
    {
        PickDestination();
        Goto('Moving');
    }

Pausing:
    Acceleration = vect(0,0,0);

    if (enemy != None)
    {
        if (HidePoint(destPoint) != None)
        {
            if (ShouldPlayTurn(Location + HidePoint(destPoint).faceDirection))
                PlayTurning();
            TurnTo(Location + HidePoint(destPoint).faceDirection);
        }
        Enable('AnimEnd');
        TweenToWaiting(0.2);
        while (AICanSee(enemy, 1.0, false, false, true, true) <= 0)
            Sleep(0.25);
        Disable('AnimEnd');
        FinishAnim();
    }

    Goto('Flee');

Cower:
    if (!InSeat(useLoc))
        Goto('CowerContinue');

    PlayRunning();
    MoveTo(useLoc, MaxDesiredSpeed);

CowerContinue:
    Acceleration = vect(0,0,0);
    PlayCowerBegin();
    FinishAnim();
    PlayCowering();

    // behavior 3 - cower and occasionally make short runs
    while (true)
    {
        Sleep(FRand()*3+6);

        PlayCowerEnd();
        FinishAnim();
        if (AIPickRandomDestination(60, 150, 0, 0, 0, 0,
                                    2, FRand()*0.3+0.6, useLoc))
        {
            if (ShouldPlayWalk(useLoc))
                PlayRunning();
            MoveTo(useLoc, MaxDesiredSpeed);
        }
        PlayCowerBegin();
        FinishAnim();
        PlayCowering();
    }

    /* behavior 2 - cower forever
    // don't stop cowering
    while (true)
        Sleep(1.0);
    */

    /* behavior 1 - cower only when enemy watching
    if (enemy != None)
    {
        while (AICanSee(enemy, 1.0, false, false, true, true) > 0)
            Sleep(0.25);
    }
    PlayCowerEnd();
    FinishAnim();
    Goto('Pausing');
    */

ContinueFlee:
ContinueFromDoor:
    FinishAnim();
    PlayRunning();
    if (bCower)
        Goto('Cower');
    else
        Goto('Moving');

}


// ----------------------------------------------------------------------
// state Attacking
//
// Kill!  Kill!  Kill!  Kill!
// ----------------------------------------------------------------------

State Attacking
{
    function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
    {
        local Pawn oldEnemy;
        local bool bHateThisInjury;
        local bool bFearThisInjury;

        if ((health > 0) && (bLookingForInjury || bLookingForIndirectInjury))
        {
            oldEnemy = Enemy;

            bHateThisInjury = ShouldReactToInjuryType(damageType, bHateInjury, bHateIndirectInjury);
            bFearThisInjury = ShouldReactToInjuryType(damageType, bFearInjury, bFearIndirectInjury);

            if (bHateThisInjury)
                IncreaseAgitation(instigatedBy, 1.0);
            if (bFearThisInjury)
                IncreaseFear(instigatedBy, 2.0);

            if (ReadyForNewEnemy())
                SetEnemy(instigatedBy);

            if (ShouldFlee())
            {
                SetDistressTimer();
                PlayCriticalDamageSound();
                if (RaiseAlarm == RAISEALARM_BeforeFleeing)
                    SetNextState('Alerting');
                else
                    SetNextState('Fleeing');
            }
            else
            {
                SetDistressTimer();
                if (oldEnemy != Enemy)
                    PlayNewTargetSound();
                SetNextState('Attacking', 'ContinueAttack');
            }
            GotoDisabledState(damageType, hitPos);
        }
    }

    function SetFall()
    {
        StartFalling('Attacking', 'ContinueAttack');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Reloading(DeusExWeapon reloadWeapon, float reloadTime)
    {
        Global.Reloading(reloadWeapon, reloadTime);
        if (bReadyToReload)
            if (IsWeaponReloading())
                if (!IsHandToHand())
                    TweenToShoot(0);
    }

    function EDestinationType PickDestination()
    {
        local vector               distVect;
        local vector               tempVect;
        local rotator              enemyDir;
        local float                magnitude;
        local float                calcMagnitude;
        local int                  iterations;
        local EDestinationType     destType;
        local NearbyProjectileList projList;

        destPoint = None;
        destLoc   = vect(0, 0, 0);
        destType  = DEST_Failure;

        if (enemy == None)
            return (destType);

        if (bCrouching && (CrouchTimer > 0))
            destType = DEST_SameLocation;

        if (destType == DEST_Failure)
        {
            if (AICanShoot(enemy, true, false, 0.025) || ActorReachable(enemy))
            {
                destType = ComputeBestFiringPosition(tempVect);
                if (destType == DEST_NewLocation)
                    destLoc = tempVect;
            }
        }

        if (destType == DEST_Failure)
        {
            MoveTarget = FindPathToward(enemy);
            if (MoveTarget != None)
            {
                if (!bDefendHome || IsNearHome(MoveTarget.Location))
                {
                    if (bAvoidHarm)
                        GetProjectileList(projList, MoveTarget.Location);
                    if (!bAvoidHarm || !IsLocationDangerous(projList, MoveTarget.Location))
                    {
                        destPoint = MoveTarget;
                        destType  = DEST_NewLocation;
                    }
                }
            }
        }

        // Default behavior, so they don't just stand there...
        if (destType == DEST_Failure)
        {
            enemyDir = Rotator(Enemy.Location - Location);
            if (AIPickRandomDestination(60, 150,
                                        enemyDir.Yaw, 0.5, enemyDir.Pitch, 0.5, 
                                        2, FRand()*0.4+0.35, tempVect))
            {
                if (!bDefendHome || IsNearHome(tempVect))
                {
                    destType = DEST_NewLocation;
                    destLoc  = tempVect;
                }
            }
        }

        return (destType);
    }

    function bool FireIfClearShot()
    {
        local DeusExWeapon dxWeapon;

        dxWeapon = DeusExWeapon(Weapon);
        if (dxWeapon != None)
        {
            if ((dxWeapon.AIFireDelay > 0) && (FireTimer > 0))
                return false;
            else if (AICanShoot(enemy, true, true, 0.025))
            {
                Weapon.Fire(0);
                FireTimer = dxWeapon.AIFireDelay;
                return true;
            }
            else
                return false;
        }
        else
            return false;
    }

    function CheckAttack(bool bPlaySound)
    {
        local bool bCriticalDamage;
        local bool bOutOfAmmo;
        local Pawn oldEnemy;
        local bool bAllianceSwitch;

        oldEnemy = enemy;

        bAllianceSwitch = false;
        if (!IsValidEnemy(enemy))
        {
            if (IsValidEnemy(enemy, false))
                bAllianceSwitch = true;
            SetEnemy(None, 0, true);
        }

        if (enemy == None)
        {
            if (Orders == 'Attacking')
            {
                FindOrderActor();
                SetEnemy(Pawn(OrderActor), 0, true);
            }
        }
        if (ReadyForNewEnemy())
            FindBestEnemy(false);
        if (enemy == None)
        {
            Enemy = oldEnemy;  // hack
            if (bPlaySound)
            {
                if (bAllianceSwitch)
                    PlayAllianceFriendlySound();
                else
                    PlayAreaSecureSound();
            }
            Enemy = None;
            if (Orders != 'Attacking')
                FollowOrders();
            else
                GotoState('Wandering');
            return;
        }

        SwitchToBestWeapon();
        if (bCrouching && (CrouchTimer <= 0) && !ShouldCrouch())
        {
            EndCrouch();
            TweenToShoot(0.15);
        }
        bCriticalDamage = False;
        bOutOfAmmo      = False;
        if (ShouldFlee())
            bCriticalDamage = True;
        else if (Weapon == None)
            bOutOfAmmo = True;
        else if (Weapon.ReloadCount > 0)
        {
            if (Weapon.AmmoType == None)
                bOutOfAmmo = True;
            else if (Weapon.AmmoType.AmmoAmount < 1)
                bOutOfAmmo = True;
        }
        if (bCriticalDamage || bOutOfAmmo)
        {
            if (bPlaySound)
            {
                if (bCriticalDamage)
                    PlayCriticalDamageSound();
                else if (bOutOfAmmo)
                    PlayOutOfAmmoSound();
            }
            if (RaiseAlarm == RAISEALARM_BeforeFleeing)
                GotoState('Alerting');
            else
                GotoState('Fleeing');
        }
        else if (bPlaySound && (oldEnemy != Enemy))
            PlayNewTargetSound();
    }

    function Tick(float deltaSeconds)
    {
        local bool   bCanSee;
        local float  yaw;
        local vector lastLocation;
        local Pawn   lastEnemy;
        local float  surpriseTime;

        Global.Tick(deltaSeconds);
        if (CrouchTimer > 0)
        {
            CrouchTimer -= deltaSeconds;
            if (CrouchTimer < 0)
                CrouchTimer = 0;
        }
        EnemyTimer += deltaSeconds;
        UpdateActorVisibility(Enemy, deltaSeconds, 1.0, false);
        if ((Enemy != None) && HasEnemyTimedOut())
        {
            lastLocation = Enemy.Location;
            lastEnemy    = Enemy;
            FindBestEnemy(true);
            if (Enemy == None)
            {
                SetSeekLocation(lastEnemy, lastLocation, SEEKTYPE_Guess, true);
                GotoState('Seeking');
            }
        }
        else if (bCanFire && (Enemy != None))
        {
            ViewRotation = Rotator(Enemy.Location-Location);
            if (bFacingTarget)
                FireIfClearShot();
            else if (!bMustFaceTarget)
            {
                yaw = (ViewRotation.Yaw-Rotation.Yaw) & 0xFFFF;
                if (yaw >= 32768)
                    yaw -= 65536;
                yaw = Abs(yaw)*360/32768;  // 0-180 x 2
                if (yaw <= FireAngle)
                    FireIfClearShot();
            }
        }
        //UpdateReactionLevel(true, deltaSeconds);
    }

    function bool IsHandToHand()
    {
        if (Weapon != None)
        {
            if (DeusExWeapon(Weapon) != None)
            {
                if (DeusExWeapon(Weapon).bHandToHand)
                    return true;
                else
                    return false;
            }
            else
                return false;
        }
        else
            return false;
    }

    function bool ReadyForWeapon()
    {
        local bool bReady;

        bReady = false;
        if (DeusExWeapon(weapon) != None)
        {
            if (DeusExWeapon(weapon).bReadyToFire)
                if (!IsWeaponReloading())
                    bReady = true;
        }
        if (!bReady)
            if (enemy == None)
                bReady = true;
        if (!bReady)
            if (!AICanShoot(enemy, true, false, 0.025))
                bReady = true;

        return (bReady);
    }

    function bool ShouldCrouch()
    {
        if (bCanCrouch && !Region.Zone.bWaterZone && !IsHandToHand() &&
            ((enemy != None) && (VSize(enemy.Location-Location) > 300)) &&
            ((DeusExWeapon(Weapon) == None) || DeusExWeapon(Weapon).bUseWhileCrouched))
            return true;
        else
            return false;
    }

    function StartCrouch()
    {
        if (!bCrouching)
        {
            bCrouching = true;
            SetBasedPawnSize(CollisionRadius, GetCrouchHeight());
            CrouchTimer = 1.0+FRand()*0.5;
        }
    }

    function EndCrouch()
    {
        if (bCrouching)
        {
            bCrouching = false;
            ResetBasedPawnSize();
        }
    }

    function BeginState()
    {
        StandUp();

        // hack
        if (MaxRange < MinRange+10)
            MaxRange = MinRange+10;
        bCanFire      = false;
        bFacingTarget = false;

        SwitchToBestWeapon();

        //EnemyLastSeen = 0;
        BlockReactions();
        bCanConverse = False;
        bAttacking = True;
        bStasis = False;
        SetDistress(true);

        CrouchTimer = 0;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bCanFire      = false;
        bFacingTarget = false;

        ResetReactions();
        bCanConverse = True;
        bAttacking = False;
        bStasis = True;
        bReadyToReload = false;

        EndCrouch();
    }

Begin:
    if (Enemy == None)
        GotoState('Seeking');
    //EnemyLastSeen = 0;
    CheckAttack(false);

Surprise:
    if ((1.0-ReactionLevel)*SurprisePeriod < 0.25)
        Goto('BeginAttack');
    Acceleration=vect(0,0,0);
    PlaySurpriseSound();
    PlayWaiting();
    while (ReactionLevel < 1.0)
    {
        TurnToward(Enemy);
        Sleep(0);
    }

BeginAttack:
    EnemyReadiness = 1.0;
    ReactionLevel  = 1.0;
    if (PlayerAgitationTimer > 0)
        PlayAllianceHostileSound();
    else
        PlayTargetAcquiredSound();
    if (PlayBeginAttack())
    {
        Acceleration = vect(0,0,0);
        TurnToward(enemy);
        FinishAnim();
    }

RunToRange:
    bCanFire       = false;
    bFacingTarget  = false;
    bReadyToReload = false;
    EndCrouch();
    if (Physics == PHYS_Falling)
        TweenToRunning(0.05);
    WaitForLanding();
    if (!IsWeaponReloading() || bCrouching)
    {
        if (ShouldPlayTurn(Enemy.Location))
            PlayTurning();
        TurnToward(enemy);
    }
    else
        Sleep(0);
    bCanFire = true;
    while (PickDestination() == DEST_NewLocation)
    {
        if (bCanStrafe && ShouldStrafe())
        {
            PlayRunningAndFiring();
            if (destPoint != None)
                StrafeFacing(destPoint.Location, enemy);
            else
                StrafeFacing(destLoc, enemy);
            bFacingTarget = true;
        }
        else
        {
            bFacingTarget = false;
            PlayRunning();
            if (destPoint != None)
                MoveToward(destPoint, MaxDesiredSpeed);
            else
                MoveTo(destLoc, MaxDesiredSpeed);
        }
        CheckAttack(true);
    }

Fire:
    bCanFire      = false;
    bFacingTarget = false;
    Acceleration = vect(0, 0, 0);

    SwitchToBestWeapon();
    if (FRand() > 0.5)
        bUseSecondaryAttack = true;
    else
        bUseSecondaryAttack = false;
    if (IsHandToHand())
        TweenToAttack(0.15);
    else if (ShouldCrouch() && (FRand() < CrouchRate))
    {
        TweenToCrouchShoot(0.15);
        FinishAnim();
        StartCrouch();
    }
    else
        TweenToShoot(0.15);
    if (!IsWeaponReloading() || bCrouching)
        TurnToward(enemy);
    FinishAnim();
    bReadyToReload = true;

ContinueFire:
    while (!ReadyForWeapon())
    {
        if (PickDestination() != DEST_SameLocation)
            Goto('RunToRange');
        CheckAttack(true);
        if (!IsWeaponReloading() || bCrouching)
            TurnToward(enemy);
        else
            Sleep(0);
    }
    CheckAttack(true);
    if (!FireIfClearShot())
        Goto('ContinueAttack');
    bReadyToReload = false;
    if (bCrouching)
        PlayCrouchShoot();
    else if (IsHandToHand())
        PlayAttack();
    else
        PlayShoot();
    FinishAnim();
    if (FRand() > 0.5)
        bUseSecondaryAttack = true;
    else
        bUseSecondaryAttack = false;
    bReadyToReload = true;
    if (!IsHandToHand())
    {
        if (bCrouching)
            TweenToCrouchShoot(0);
        else
            TweenToShoot(0);
    }
    CheckAttack(true);
    if (PickDestination() != DEST_NewLocation)
    {
        if (!IsWeaponReloading() || bCrouching)
            TurnToward(enemy);
        else
            Sleep(0);
        Goto('ContinueFire');
    }
    Goto('RunToRange');

ContinueAttack:
ContinueFromDoor:
    CheckAttack(true);
    if (PickDestination() != DEST_NewLocation)
        Goto('Fire');
    else
        Goto('RunToRange');

}


// ----------------------------------------------------------------------
// state Alerting
//
// Warn other NPCs that an enemy is about
// ----------------------------------------------------------------------

State Alerting
{
    function SetFall()
    {
        StartFalling('Alerting', 'ContinueAlert');
    }

    function Tick(float deltaSeconds)
    {
        Global.Tick(deltaSeconds);
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Bump(actor bumper)
    {
        if (bAcceptBump)
        {
            if (bumper == AlarmActor)
            {
                bAcceptBump = False;
                GotoState('Alerting', 'SoundAlarm');
            }
        }

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function bool IsAlarmReady(Actor actorAlarm)
    {
        local bool      bReady;
        local AlarmUnit alarm;

        bReady = false;
        alarm = AlarmUnit(actorAlarm);
        if ((alarm != None) && !alarm.bDeleteMe)
            if (!alarm.bActive)
                if ((alarm.associatedPawn == None) ||
                    (alarm.associatedPawn == self))
                    bReady = true;

        return bReady;
    }

    function TriggerAlarm()
    {
        if ((AlarmActor != None) && !AlarmActor.bDeleteMe)
        {
            if (AlarmActor.hackStrength > 0)  // make sure the alarm hasn't been hacked
                AlarmActor.Trigger(self, Enemy);
        }
    }

    function bool IsAlarmInRange(AlarmUnit alarm)
    {
        local bool bInRange;

        bInRange = false;
        if ((alarm != None) && !alarm.bDeleteMe)
            if ((VSize((alarm.Location-Location)*vect(1,1,0)) <
                 (CollisionRadius+alarm.CollisionRadius+24)) &&
                (Abs(alarm.Location.Z-Location.Z) < (CollisionHeight+alarm.CollisionHeight)))
                bInRange = true;

        return (bInRange);
    }

    function vector FindAlarmPosition(Actor alarm)
    {
        local vector alarmPos;

        alarmPos = alarm.Location;
        alarmPos += vector(alarm.Rotation.Yaw*rot(0,1,0))*(CollisionRadius+alarm.CollisionRadius);

        return (alarmPos);
    }

    function bool GetNextAlarmPoint(AlarmUnit alarm)
    {
        local vector alarmPoint;
        local bool   bValid;

        destPoint = None;
        destLoc   = vect(0,0,0);
        bValid    = false;

        if ((alarm != None) && !alarm.bDeleteMe)
        {
            alarmPoint = FindAlarmPosition(alarm);
            if (PointReachable(alarmPoint))
            {
                destLoc = alarmPoint;
                bValid = true;
            }
            else
            {
                MoveTarget = FindPathTo(alarmPoint);
                if (MoveTarget != None)
                {
                    destPoint = MoveTarget;
                    bValid = true;
                }
            }
        }

        return (bValid);
    }

    function AlarmUnit FindTarget()
    {
        local ScriptedPawn pawnAlly;
        local AlarmUnit    alarm;
        local float        dist;
        local AlarmUnit    bestAlarm;
        local float        bestDist;

        bestAlarm = None;

        // Do we have any allies on this level?
        foreach AllActors(Class'ScriptedPawn', pawnAlly)
            if (GetPawnAllianceType(pawnAlly) == ALLIANCE_Friendly)
                break;

        // Yes, so look for an alarm box that isn't active...
        if (pawnAlly != None)
        {
            foreach RadiusActors(Class'AlarmUnit', alarm, 2400)
            {
                if (GetAllianceType(alarm.Alliance) != ALLIANCE_Hostile)
                {
                    dist = VSize((Location-alarm.Location)*vect(1,1,2));  // use squished sphere
                    if ((bestAlarm == None) || (dist < bestDist))
                    {
                        bestAlarm = alarm;
                        bestDist  = dist;
                    }
                }
            }

            // Is the nearest alarm already going off?  And can we reach it?
            if (!IsAlarmReady(bestAlarm) || !GetNextAlarmPoint(bestAlarm))
                bestAlarm = None;
        }

        // Return our target alarm box
        return (bestAlarm);
    }

    function bool PickDestination()
    {
        local bool      bDest;
        local AlarmUnit alarm;

        // Init
        destPoint = None;
        destLoc   = vect(0, 0, 0);
        bDest     = false;

        // Find an alarm we can trigger
        alarm = FindTarget();
        if (alarm != None)
        {
            // Find a way to get there
            AlarmActor = alarm;
            alarm.associatedPawn = self;
            bDest = true;  // if alarm != none, we've already computed the route to the alarm
        }

        // Return TRUE if we were successful
        return (bDest);
    }

    function BeginState()
    {
        StandUp();
        //Disable('AnimEnd');
        bAcceptBump = False;
        bCanConverse = False;
        AlarmActor = None;
        bStasis = False;
        BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        EnemyReadiness = 1.0;
        ReactionLevel  = 1.0;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        ResetReactions();
        bAcceptBump = False;
        //Enable('AnimEnd');
        bCanConverse = True;
        if (AlarmActor != None)
            if (AlarmActor.associatedPawn == self)
                AlarmActor.associatedPawn = None;
        AlarmActor = None;
        bStasis = True;
    }

Begin:
    if (Enemy == None)
        GotoState('Seeking');
    //EnemyLastSeen = 0;
    destPoint = None;
    if (RaiseAlarm == RAISEALARM_Never)
        GotoState('Fleeing');
    if (AlarmTimer > 0)
        PlayGoingForAlarmSound();

Alert:
    if (AlarmTimer > 0)
        Goto('Done');

    WaitForLanding();
    if (!PickDestination())
        Goto('Done');

Moving:
    // Can we go somewhere?
    bAcceptBump = True;
    EnableCheckDestLoc(true);
    while (true)
    {
        if (destPoint != None)
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
            CheckDestLoc(MoveTarget.Location, true);
        }
        else
        {
            if (ShouldPlayWalk(destLoc))
                PlayRunning();
            MoveTo(destLoc, MaxDesiredSpeed);
            CheckDestLoc(destLoc);
        }
        if (IsAlarmInRange(AlarmActor))
            break;
        else if (!GetNextAlarmPoint(AlarmActor))
            break;
    }
    EnableCheckDestLoc(false);

SoundAlarm:
    Acceleration=vect(0,0,0);
    bAcceptBump = False;
    if (IsAlarmInRange(AlarmActor))
    {
        TurnToward(AlarmActor);
        PlayPushing();
        FinishAnim();
        TriggerAlarm();
    }

Done:
    bAcceptBump = False;
    if (RaiseAlarm == RAISEALARM_BeforeAttacking)
        GotoState('Attacking');
    else
        GotoState('Fleeing');

ContinueAlert:
ContinueFromDoor:
    Goto('Alert');

}


// ----------------------------------------------------------------------
// state Shadowing
//
// Quietly tail another character
// ----------------------------------------------------------------------

State Shadowing
{
    function SetFall()
    {
        StartFalling('Shadowing', 'ContinueShadow');
    }

    function Tick(float deltaSeconds)
    {
        local bool  bMove;
        local float deltaValue;

        Global.Tick(deltaSeconds);

        deltaValue = deltaSeconds;

        // If we're running, and we can see our target, STOP RUNNING!
        if (bRunningStealthy)
        {
            UpdateActorVisibility(orderActor, deltaValue, 0.0, false);
            deltaValue = 0;
            if (EnemyLastSeen <= 0)
            {
                bRunningStealthy = False;
                PlayWalking();
                DesiredSpeed = GetWalkingSpeed();
            }
        }

        // Are we stopped?
        if (bPausing)
        {
            // Can we see our target?
            bMove = False;
            UpdateActorVisibility(orderActor, deltaValue, 0.5, false);
            deltaValue = 0;

            // No -- move toward him!
            if (EnemyLastSeen > 0.5)
                bMove = True;

            // We can see him, and we're staring...
            else if (bStaring)
            {
                // ...can he see us staring at him?
                if ((Pawn(orderActor) != None) &&
                    (Pawn(orderActor).AICanSee(self, , false, true, false, false) > 0))
                    bMove = True;  // Time to look inconspicuous
            }

            // Move if we need to
            if (bMove)
            {
                if (bStaring)
                    GotoState('Shadowing', 'StopStaring');
                else
                    GotoState('Shadowing', 'StopPausing');
                bPausing = False;
                bStaring = False;
            }
        }
    }

    function Bump(actor bumper)
    {
        if (bAcceptBump)
        {
            // If we get bumped by another actor while we wait, start wandering again
            bAcceptBump = False;
            bPausing = False;
            bStaring = False;
            Disable('AnimEnd');
            GotoState('Shadowing', 'Shadow');
        }

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function float DistanceToTarget()
    {
        return (VSize(Location-orderActor.Location));
    }

    function bool PickDestination()
    {
        local Actor   destActor;
        local Vector  distVect;
        local Rotator relativeRotation;
        local float   magnitude;
        local float   minDist;
        local float   maxDist;
        local float   bestDist;
        local bool    bDest;
        local float   dist;

        // Init
        destPoint = None;
        destLoc   = vect(0, 0, 0);

        // Conveniences
        destActor = orderActor;
        minDist   = 400;
        maxDist   = 700;
        bestDist  = (maxDist+minDist)*0.5;

        distVect  = Location - destActor.Location;
        magnitude = VSize(distVect);

        bDest = False;

        // Can we see the target?
        if (AICanSee(destActor, , false, false, false, true) > 0)
        {
            relativeRotation = Rotator(distVect);

            // How far will we go?
            dist = (wanderlust*300+150) * (FRand()*0.2+0.9); // 150-450, +/-10%

            // Move around inconspicuously, like we're just wandering
            if (magnitude < minDist)  // too close -- move away
                bDest = AIPickRandomDestination(100, dist,
                                                relativeRotation.Yaw, 0.8, relativeRotation.Pitch, 0.8,
                                                3, FRand()*0.4+0.35, destLoc);

            else if (magnitude < maxDist)  // just right -- move normally
                bDest = AIPickRandomDestination(100, dist,
                                                relativeRotation.Yaw+32768, 0, -relativeRotation.Pitch, 0,
                                                2, FRand()*0.4+0.35, destLoc);

            else  // too far -- move closer
                bDest = AIPickRandomDestination(100, dist,
                                                relativeRotation.Yaw+32768, 0.8, -relativeRotation.Pitch, 0.8,
                                                3, FRand()*0.4+0.35, destLoc);
        }

        // Nope -- find a path towards him
        else
        {
            MoveTarget = FindPathToward(destActor);
            if (MoveTarget != None)
            {
                if (!MoveTarget.Region.Zone.bWaterZone && (MoveTarget.Physics != PHYS_Falling))
                {
                    destPoint = MoveTarget;
                    bDest = True;
                }
            }
        }

        // Return TRUE if we found a place to go
        return (bDest);

    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        bRunningStealthy = False;
        bPausing = False;
        bStaring = False;
        bStasis = False;
        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = False;
        Enable('AnimEnd');
        bRunningStealthy = False;
        bPausing = False;
        bStaring = False;
        bStasis = True;
    }

Begin:
    EnemyLastSeen = 0;
    destPoint = None;

Shadow:
    WaitForLanding();

Moving:
    Sleep(0.0);

    // Can we go somewhere?
    if (PickDestination())
    {
        // Are we going to a navigation point?
        if (destPoint != None)
        {
            if (MoveTarget != None)
            {
                // Run if we're too far away, and we can't see our target
                if ((DistanceToTarget() > 900) &&
                    (AICanSee(orderActor, , false, true, true, true) <= 0))
                {
                    bRunningStealthy = True;
                    if (ShouldPlayWalk(MoveTarget.Location))
                        PlayRunning();
                    MoveToward(MoveTarget, MaxDesiredSpeed);
                }

                // Otherwise, walk nonchalantly
                else
                {
                    bRunningStealthy = False;
                    if (ShouldPlayWalk(MoveTarget.Location))
                        PlayWalking();
                    MoveToward(MoveTarget, GetWalkingSpeed());
                }
            }
        }

        // No pathnode, so walk to a point
        else
        {
            bRunningStealthy = False;
            if (ShouldPlayWalk(destLoc))
                PlayWalking();
            MoveTo(destLoc, GetWalkingSpeed());
        }
    }

    // Can we see the target?  If not, keep walking
    if (AICanSee(orderActor, , false, false, false, true) <= 0)
        Goto('Moving');

Pausing:
    // Stop
    bRunningStealthy = False;
    Acceleration = vect(0, 0, 0);

    // Can the target see us?  If not, stare!
    if (orderActor.IsA('Pawn') && Pawn(orderActor).AICanSee(self, , false, true, false, false) <= 0)
        Goto('Staring');

    // Stop normally
    sleepTime = 6.0;
    Enable('AnimEnd');
    TweenToWaiting(0.2);
    bAcceptBump = True;
    sleepTime *= (-0.9*restlessness) + 1;
    bStaring = False;
    bPausing = True;
    Sleep(sleepTime);

StopPausing:
    // Time to move again
    bPausing = False;
    bStaring = False;
    Disable('AnimEnd');
    bAcceptBump = False;
    FinishAnim();
    Goto('Shadow');

Staring:
    // Stare at the target
    PlayTurning();
    TurnToward(orderActor);

    Enable('AnimEnd');
    TweenToWaiting(0.2);

    // Don't move 'til he looks at us
    bAcceptBump = True;
    bStaring = True;
    bPausing = True;
    while (true)
    {
        PlayTurning();
        TurnToward(orderActor);
        TweenToWaiting(0.2);
        Sleep(0.25);
    }

StopStaring:
    // He's looking, or we can't see him -- time to move
    bPausing = False;
    bStaring = False;
    Disable('AnimEnd');
    bAcceptBump = False;
    FinishAnim();
    Goto('Shadow');

ContinueShadow:
ContinueFromDoor:
    FinishAnim();
    PlayRunning();
    Goto('Moving');
}


// ----------------------------------------------------------------------
// state Following
//
// Follow an actor
// ----------------------------------------------------------------------

state Following
{
    function SetFall()
    {
        StartFalling('Following', 'ContinueFollow');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Tick(float deltaSeconds)
    {
        Global.Tick(deltaSeconds);

        if (BackpedalTimer >= 0)
            BackpedalTimer += deltaSeconds;

        animTimer[1] += deltaSeconds;
        if ((Physics == PHYS_Walking) && (orderActor != None))
        {
            if (Acceleration == vect(0,0,0))
                LookAtActor(orderActor, true, true, true, 0, 0.25);
            else
                PlayTurnHead(LOOK_Forward, 1.0, 0.25);
        }
    }

    function bool PickDestination()
    {
        local float   dist;
        local float   extra;
        local float   distMax;
        local int     dir;
        local rotator rot;
        local bool    bSuccess;

        bSuccess = false;
        destPoint = None;
        destLoc   = vect(0, 0, 0);
        extra = orderActor.CollisionRadius + CollisionRadius;
        dist = VSize(orderActor.Location - Location);
        dist -= extra;
        if (dist < 0)
            dist = 0;

        if ((dist > 180) || (AICanSee(orderActor, , false, false, false, true) <= 0))
        {
            if (ActorReachable(orderActor))
            {
                rot = Rotator(orderActor.Location - Location);
                distMax = (dist-180)+45;
                if (distMax > 80)
                    distMax = 80;
                bSuccess = AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 0, distMax, destLoc);
            }
            else
            {
                MoveTarget = FindPathToward(orderActor);
                if (MoveTarget != None)
                {
                    destPoint = MoveTarget;
                    bSuccess = true;
                }
            }
            BackpedalTimer = -1;
        }
        else if (dist < 60)
        {
            if (BackpedalTimer < 0)
                BackpedalTimer = 0;
            if (BackpedalTimer > 1.0)  // give the player enough time to converse, if he wants to
            {
                rot = Rotator(Location - orderActor.Location);
                bSuccess = AIDirectionReachable(orderActor.Location, rot.Yaw, rot.Pitch, 60+extra, 120+extra, destLoc);
            }
        }
        else
            BackpedalTimer = -1;

        return (bSuccess);
    }

    function BeginState()
    {
        StandUp();
        //Disable('AnimEnd');
        bStasis = False;
        SetupWeapon(false);
        SetDistress(false);
        BackpedalTimer = -1;
        SeekPawn = None;
        EnableCheckDestLoc(true);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = False;
        //Enable('AnimEnd');
        bStasis = True;
        StopBlendAnims();
    }

Begin:
    Acceleration = vect(0, 0, 0);
    destPoint = None;
    if (orderActor == None)
        GotoState('Standing');

    if (!PickDestination())
        Goto('Wait');

Follow:
    if (destPoint != None)
    {
        if (MoveTarget != None)
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
            CheckDestLoc(MoveTarget.Location, true);
        }
        else
            Sleep(0.0);  // this shouldn't happen
    }
    else
    {
        if (ShouldPlayWalk(destLoc))
            PlayRunning();
        MoveTo(destLoc, MaxDesiredSpeed);
        CheckDestLoc(destLoc);
    }
    if (PickDestination())
        Goto('Follow');

Wait:
    //PlayTurning();
    //TurnToward(orderActor);
    PlayWaiting();

WaitLoop:
    Acceleration=vect(0,0,0);
    Sleep(0.0);
    if (!PickDestination())
        Goto('WaitLoop');
    else
        Goto('Follow');

ContinueFollow:
ContinueFromDoor:
    Acceleration=vect(0,0,0);
    if (PickDestination())
        Goto('Follow');
    else
        Goto('Wait');

}


// ----------------------------------------------------------------------
// state WaitingFor
//
// Wait for a pawn to become visible, then move to it
// ----------------------------------------------------------------------

state WaitingFor
{
    function SetFall()
    {
        StartFalling('WaitingFor', 'ContinueFollow');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Bump(actor bumper)
    {
        // If we hit the guy we're going to, end the state
        if (bumper == OrderActor)
            GotoState('WaitingFor', 'Done');

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function Touch(actor toucher)
    {
        // If we hit the guy we're going to, end the state
        if (toucher == OrderActor)
            GotoState('WaitingFor', 'Done');

        // Handle conversations, if need be
        Global.Touch(toucher);
    }

    function BeginState()
    {
        StandUp();
        //BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        bStasis = True;
        SeekPawn = None;
        EnableCheckDestLoc(true);
    }
    function EndState()
    {
        EnableCheckDestLoc(false);
        //ResetReactions();
        bStasis = True;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    if (orderActor == None)
        GotoState('Idle');
    PlayWaiting();

Wait:
    Sleep(1.0);
    if (AICanSee(orderActor, 1.0, false, true, false, true) <= 0)
        Goto('Wait');
    bStasis = False;

Follow:
    if (IsOverlapping(orderActor))
        Goto('Done');
    MoveTarget = GetNextWaypoint(orderActor);
    if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) &&
        (MoveTarget.Physics != PHYS_Falling))
    {
        if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn'))
        {
            if (GetNextVector(orderActor, useLoc))
            {
                if (ShouldPlayWalk(useLoc))
                    PlayRunning();
                MoveTo(useLoc, MaxDesiredSpeed);
                CheckDestLoc(useLoc);
            }
            else
                Goto('Pause');
        }
        else
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
            CheckDestLoc(MoveTarget.Location, true);
        }
        if (IsOverlapping(orderActor))
            Goto('Done');
        else
            Goto('Follow');
    }

Pause:
    Acceleration = vect(0, 0, 0);
    TurnToward(orderActor);
    PlayWaiting();
    Sleep(1.0);
    Goto('Follow');

Done:
    GotoState('Standing');

ContinueFollow:
ContinueFromDoor:
    PlayRunning();
    Goto('Follow');
}


// ----------------------------------------------------------------------
// state GoingTo
//
// Move to an actor.
// ----------------------------------------------------------------------

state GoingTo
{
    function SetFall()
    {
        StartFalling('GoingTo', 'ContinueGo');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Bump(actor bumper)
    {
        // If we hit the guy we're going to, end the state
        if (bumper == OrderActor)
            GotoState('GoingTo', 'Done');

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function Touch(actor toucher)
    {
        // If we hit the guy we're going to, end the state
        if (toucher == OrderActor)
            GotoState('GoingTo', 'Done');

        // Handle conversations, if need be
        Global.Touch(toucher);
    }

    function BeginState()
    {
        StandUp();
        //BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        bStasis = False;
        SeekPawn = None;
        EnableCheckDestLoc(true);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        //ResetReactions();
        bStasis = True;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    if (orderActor == None)
        Goto('Done');

Follow:
    if (IsOverlapping(orderActor))
        Goto('Done');
    MoveTarget = GetNextWaypoint(orderActor);
    if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) &&
        (MoveTarget.Physics != PHYS_Falling))
    {
        if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn'))
        {
            if (GetNextVector(orderActor, useLoc))
            {
                if (ShouldPlayWalk(useLoc))
                    PlayWalking();
                MoveTo(useLoc, GetWalkingSpeed());
                CheckDestLoc(useLoc);
            }
            else
                Goto('Pause');
        }
        else
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayWalking();
            MoveToward(MoveTarget, GetWalkingSpeed());
            CheckDestLoc(MoveTarget.Location, true);
        }
        if (IsOverlapping(orderActor))
            Goto('Done');
        else
            Goto('Follow');
    }

Pause:
    Acceleration = vect(0, 0, 0);
    TurnToward(orderActor);
    PlayWaiting();
    Sleep(1.0);
    Goto('Follow');

Done:
    if (orderActor.IsA('PatrolPoint'))
        TurnTo(Location + PatrolPoint(orderActor).lookdir);
    GotoState('Standing');

ContinueGo:
ContinueFromDoor:
    PlayWalking();
    Goto('Follow');
}


// ----------------------------------------------------------------------
// state RunningTo
//
// Move to an actor really fast.
// ----------------------------------------------------------------------

state RunningTo
{
    function SetFall()
    {
        StartFalling('RunningTo', 'ContinueRun');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function Bump(actor bumper)
    {
        // If we hit the guy we're going to, end the state
        if (bumper == OrderActor)
            GotoState('RunningTo', 'Done');

        // Handle conversations, if need be
        Global.Bump(bumper);
    }

    function Touch(actor toucher)
    {
        // If we hit the guy we're going to, end the state
        if (toucher == OrderActor)
            GotoState('RunningTo', 'Done');

        // Handle conversations, if need be
        Global.Touch(toucher);
    }

    function BeginState()
    {
        StandUp();
        //BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        bStasis = False;
        SeekPawn = None;
        EnableCheckDestLoc(true);
    }
    function EndState()
    {
        EnableCheckDestLoc(false);
        //ResetReactions();
        bStasis = True;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    if (orderActor == None)
        Goto('Done');

Follow:
    if (IsOverlapping(orderActor))
        Goto('Done');
    MoveTarget = GetNextWaypoint(orderActor);
    if ((MoveTarget != None) && (!MoveTarget.Region.Zone.bWaterZone) &&
        (MoveTarget.Physics != PHYS_Falling))
    {
        if ((MoveTarget == orderActor) && MoveTarget.IsA('Pawn'))
        {
            if (GetNextVector(orderActor, useLoc))
            {
                if (ShouldPlayWalk(useLoc))
                    PlayRunning();
                MoveTo(useLoc, MaxDesiredSpeed);
                CheckDestLoc(useLoc);
            }
            else
                Goto('Pause');
        }
        else
        {
            if (ShouldPlayWalk(MoveTarget.Location))
                PlayRunning();
            MoveToward(MoveTarget, MaxDesiredSpeed);
            CheckDestLoc(MoveTarget.Location, true);
        }
        if (IsOverlapping(orderActor))
            Goto('Done');
        else
            Goto('Follow');
    }

Pause:
    Acceleration = vect(0, 0, 0);
    TurnToward(orderActor);
    PlayWaiting();
    Sleep(1.0);
    Goto('Follow');

Done:
    if (orderActor.IsA('PatrolPoint'))
        TurnTo(Location + PatrolPoint(orderActor).lookdir);
    GotoState('Standing');

ContinueRun:
ContinueFromDoor:
    PlayRunning();
    Goto('Follow');
}


// ----------------------------------------------------------------------
// state DebugFollowing
//
// Following state used for pathnode testing
// ----------------------------------------------------------------------

state DebugFollowing
{
    function SetFall()
    {
        StartFalling('DebugFollowing', 'ContinueRun');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function BeginState()
    {
        StandUp();
        BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        bStasis = false;
        EnableCheckDestLoc(false);
    }
    function EndState()
    {
        EnableCheckDestLoc(false);
        ResetReactions();
        bStasis = true;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    if (orderActor == None)
        Goto('Done');

Follow:
    MoveTarget = GetNextWaypoint(orderActor);
    if (MoveTarget != None)
    {
        if (ShouldPlayWalk(MoveTarget.Location))
            PlayRunning();
        MoveToward(MoveTarget, 1.0);
        Goto('Follow');
    }

Pause:
    Acceleration = vect(0, 0, 0);
    TurnToward(orderActor);
    PlayWaiting();
    Sleep(1.0);
    Goto('Follow');

Done:
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Standing');

ContinueRun:
ContinueFromDoor:
    PlayRunning();
    Goto('Follow');
}


// ----------------------------------------------------------------------
// state DebugPathfinding
//
// Following state used for pathnode testing
// ----------------------------------------------------------------------

state DebugPathfinding
{
    function SetFall()
    {
        StartFalling('DebugPathfinding', 'ContinueRun');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function BeginState()
    {
        StandUp();
        BlockReactions();
        SetupWeapon(false);
        SetDistress(false);
        bStasis = false;
        EnableCheckDestLoc(false);
    }
    function EndState()
    {
        EnableCheckDestLoc(false);
        ResetReactions();
        bStasis = true;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    if (orderActor == None)
        Goto('Done');

Follow:
    MoveTarget = FindPathToward(orderActor);
    if (MoveTarget != None)
    {
        if (ShouldPlayWalk(MoveTarget.Location))
            PlayRunning();
        MoveToward(MoveTarget, 1.0);
        Goto('Follow');
    }

Pause:
    Acceleration = vect(0, 0, 0);
    TurnToward(orderActor);
    PlayWaiting();
    Sleep(1.0);
    Goto('Follow');

Done:
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Standing');

ContinueRun:
ContinueFromDoor:
    PlayRunning();
    Goto('Follow');
}


// ----------------------------------------------------------------------
// state Burning
//
// Whoa-oh-oh, I'm on fire.
// ----------------------------------------------------------------------

state Burning
{
    function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
    {
        local name newLabel;

        if (health > 0)
        {
            if (enemy != instigatedBy)
            {
                SetEnemy(instigatedBy);
                newLabel = 'NewEnemy';
            }
            else
                newLabel = 'ContinueBurn';

            if ( Enemy != None )
                LastSeenPos = Enemy.Location;
            SetNextState('Burning', newLabel);
            if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned'))
                GotoDisabledState(damageType, hitPos);
        }
    }

    function SetFall()
    {
        StartFalling('Burning', 'ContinueBurn');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function PickDestination()
    {
        local float           magnitude;
        local float           distribution;
        local int             yaw, pitch;
        local Rotator         rotator1;
        local NavigationPoint nav;
        local float           dist;
        local NavigationPoint bestNav;
        local float           bestDist;

        destPoint = None;
        bestNav   = None;
        bestDist  = 2000;   // max distance to water

        // Seek out water
        if (bCanSwim)
        {
            nav = Level.NavigationPointList;
            while (nav != None)
            {
                if (nav.Region.Zone.bWaterZone)
                {
                    dist = VSize(Location - nav.Location);
                    if (dist < bestDist)
                    {
                        bestNav  = nav;
                        bestDist = dist;
                    }
                }
                nav = nav.nextNavigationPoint;
            }
        }

        if (bestNav != None)
        {
            // It'd be nice if we could traverse all pathnodes and figure out their
            // distances...  unfortunately, it's too slow.  :(

            MoveTarget = FindPathToward(bestNav);
            if (MoveTarget != None)
            {
                destPoint = bestNav;
                destLoc   = bestNav.Location;
            }
        }

        // Can't get to water -- run willy-nilly
        if (destPoint == None)
        {
            if (Enemy == None)
            {
                yaw = 0;
                pitch = 0;
                distribution = 0;
            }
            else
            {
                rotator1 = Rotator(Location-Enemy.Location);
                yaw = rotator1.Yaw;
                pitch = rotator1.Pitch;
                distribution = 0.5;
            }

            magnitude = 300*(FRand()*0.4+0.8);  // 400, +/-20%
            if (!AIPickRandomDestination(100, magnitude, yaw, distribution, pitch, distribution, 4,
                                         FRand()*0.4+0.35, destLoc))
                destLoc = Location+(VRand()*200);  // we give up
        }
    }

    function BeginState()
    {
        StandUp();
        BlockReactions();
        bCanConverse = False;
        SetupWeapon(false, true);
        bStasis = False;
        SetDistress(true);
        EnemyLastSeen = 0;
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        ResetReactions();
        bCanConverse = True;
        bStasis = True;
    }

Begin:
    if (!bOnFire)
        Goto('Done');
    PlayOnFireSound();

NewEnemy:
    Acceleration = vect(0, 0, 0);

Run:
    if (!bOnFire)
        Goto('Done');
    PlayPanicRunning();
    PickDestination();
    if (destPoint != None)
    {
        MoveToward(MoveTarget, MaxDesiredSpeed);
        while ((MoveTarget != None) && (MoveTarget != destPoint))
        {
            MoveTarget = FindPathToward(destPoint);
            if (MoveTarget != None)
                MoveToward(MoveTarget, MaxDesiredSpeed);
        }
    }
    else
        MoveTo(destLoc, MaxDesiredSpeed);
    Goto('Run');

Done:
    if (IsValidEnemy(Enemy))
        HandleEnemy();
    else
        FollowOrders();

ContinueBurn:
ContinueFromDoor:
    Goto('Run');
}


// ----------------------------------------------------------------------
// state AvoidingProjectiles
//
// Run away from a projectile.
// ----------------------------------------------------------------------

state AvoidingProjectiles
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('RunningTo', 'ContinueRun');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function PickDestination(bool bGotoWatch)
    {
        local NearbyProjectileList projList;
        local bool                 bMove;
        local vector               projVector;
        local rotator              projRot;
        local int                  i;
        local int                  bestSlot;
        local float                bestDist;

        destLoc   = vect(0,0,0);
        destPoint = None;
        bMove = false;

        if (GetProjectileList(projList, Location) > 0)
        {
            if (IsLocationDangerous(projList, Location))
            {
                projVector = ComputeAwayVector(projList);
                projRot    = Rotator(projVector);
                if (AIDirectionReachable(Location, projRot.Yaw, projRot.Pitch, CollisionRadius+24, VSize(projVector), destLoc))
                {
                    useLoc = Location + vect(0,0,1)*BaseEyeHeight;  // hack
                    bMove = true;
                }
            }
        }

        if (bMove)
            GotoState('AvoidingProjectiles', 'RunAway');
        else if (bGotoWatch)
            GotoState('AvoidingProjectiles', 'Watch');
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        bCanJump = false;
        SetReactions(true, true, true, true, false, true, true, true, true, true, true, true);
        bStasis = False;
        useLoc = Location + vect(0,0,1)*BaseEyeHeight + Vector(Rotation);
        bCanConverse = False;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        if (JumpZ > 0)
            bCanJump = true;
        ResetReactions();
        bStasis = True;
        bCanConverse = True;
    }

Begin:
    Acceleration = vect(0,0,0);
    PickDestination(true);

RunAway:
    PlayTurnHead(LOOK_Forward, 1.0, 0.0001);
    if (ShouldPlayWalk(destLoc))
        PlayRunning();
    MoveTo(destLoc, MaxDesiredSpeed);
    PickDestination(true);

Watch:
    Acceleration = vect(0,0,0);
    PlayWaiting();
    LookAtVector(useLoc, true, false, true);
    TurnTo(Vector(DesiredRotation)*1000+Location);
    sleepTime = 3.0;
    while (sleepTime > 0)
    {
        sleepTime -= 0.5;
        Sleep(0.5);
        PickDestination(false);
    }

Done:
    if (Orders != 'AvoidingProjectiles')
        FollowOrders();
    else
        GotoState('Wandering');

ContinueRun:
ContinueFromDoor:
    PickDestination(false);
    Goto('Done');

}


// ----------------------------------------------------------------------
// state AvoidingPawn
//
// Run away from an onrushing pawn (used for small, dumb critters only).
// ----------------------------------------------------------------------

state AvoidingPawn
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('AvoidingPawn', 'ContinueAvoid');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function PickDestination()
    {
        local int     iterations;
        local float   magnitude;
        local rotator rot;
        local float   speed;
        local float   time;
        local vector  newPos;
        local float   minDist;

        minDist = 20;
        speed = VSize(Enemy.Velocity);
        if (speed == 0)
            time = 1;
        else
            time  = VSize(Location - Enemy.Location)/speed;
        newPos = Enemy.Location + Enemy.Velocity*(time*0.98);

        magnitude  = 100*(FRand()*0.2+0.9);  // 120, +/-10%
        rot        = Rotator(Location-newPos);
        iterations = 2;
        if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc))
        {
            rot = Rotator(Location - Enemy.Location);
            if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc))
            {
                if (speed > 0)
                    rot = Rotator(Enemy.Velocity);
                else
                    rot = Enemy.Rotation;
                if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc))
                {
                    rot.Yaw   = -rot.Yaw;
                    rot.Pitch = -rot.Pitch;
                    if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, minDist, magnitude, destLoc))
                        destLoc = Location;  // we give up
                }
            }
        }
    }

    function BeginState()
    {
        StandUp();
        bCanJump = false;
        bStasis = False;
        SetupWeapon(false);
        SetDistress(false);
        SeekPawn = None;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = True;
        if (JumpZ > 0)
            bCanJump = true;
        bStasis = True;
    }

Begin:
    if (!ShouldBeStartled(Enemy))
        Goto('Done');
    Goto('Avoid');

ContinueFromDoor:
    Goto('Avoid');

Avoid:
ContinueAvoid:
    if (!ShouldBeStartled(Enemy))
        Goto('Done');
    PickDestination();
    if (destLoc == Location)
        Goto('Pause');
    if (ShouldPlayWalk(destLoc))
        PlayRunning();
    MoveTo(destLoc, MaxDesiredSpeed);
    Goto('Avoid');

Pause:
    PlayWaiting();
    Sleep(0.0);
    Goto('Avoid');

Done:
    if (Orders != 'AvoidingPawn')
        FollowOrders();
    else
        GotoState('Wandering');
}


// ----------------------------------------------------------------------
// state BackingOff
//
// Hack state used to back off when the NPC gets stuck.
// ----------------------------------------------------------------------

state BackingOff
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling('BackingOff', 'ContinueRun');
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        CheckOpenDoor(HitNormal, Wall);
    }

    function bool PickDestination()
    {
        local bool    bSuccess;
        local float   magnitude;
        local rotator rot;

        magnitude = 300;

        rot = Rotator(Destination-Location);
        bSuccess = AIPickRandomDestination(64, magnitude, rot.Yaw+32768, 0.8, -rot.Pitch, 0.8, 3,
                                           0.9, useLoc);

        return bSuccess;
    }

    function bool HandleTurn(Actor Other)
    {
        GotoState('BackingOff', 'Pause');
        return false;
    }

    function BeginState()
    {
        StandUp();
        BlockReactions();
        bStasis = False;
        bInTransientState = True;
        EnableCheckDestLoc(false);
        bCanJump = false;
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        if (JumpZ > 0)
            bCanJump = true;
        ResetReactions();
        bStasis = True;
        bInTransientState = false;
    }

Begin:
    useRot = Rotation;
    if (!PickDestination())
        Goto('Pause');
    Acceleration = vect(0,0,0);

MoveAway:
    if (ShouldPlayWalk(useLoc))
        PlayRunning();
    MoveTo(useLoc, MaxDesiredSpeed);

Pause:
    Acceleration = vect(0,0,0);
    PlayWaiting();
    Sleep(FRand()*2+2);

Done:
    if (HasNextState())
        GotoNextState();
    else
        FollowOrders();  // THIS IS BAD!!!

ContinueRun:
ContinueFromDoor:
    Goto('Done');

}


// ----------------------------------------------------------------------
// state OpeningDoor
//
// Open a door.
// ----------------------------------------------------------------------

state OpeningDoor
{
    ignores EnemyNotVisible;

    function SetFall()
    {
        StartFalling(NextState, NextLabel);
    }

    function HitWall(vector HitNormal, actor Wall)
    {
        if (Physics == PHYS_Falling)
            return;
        Global.HitWall(HitNormal, Wall);
        if (Target == Wall)
            CheckOpenDoor(HitNormal, Wall);
    }

    function bool DoorEncroaches()
    {
        local bool        bEncroaches;
        local DeusExMover dxMover;

        bEncroaches = true;
        dxMover = DeusExMover(Target);
        if (dxMover != None)
        {
            if (IsDoor(dxMover) && (dxMover.MoverEncroachType == ME_IgnoreWhenEncroach))
                bEncroaches = false;
        }

        return bEncroaches;
    }

    function FindBackupPoint()
    {
        local vector hitNorm;
        local rotator rot;
        local vector center;
        local vector area;
        local vector relPos;
        local float  distX, distY;
        local float  dist;

        hitNorm = Normal(destLoc);
        rot = Rotator(hitNorm);
        DeusExMover(Target).ComputeMovementArea(center, area);
        area.X += CollisionRadius + 30;
        area.Y += CollisionRadius + 30;
        //area.Z += CollisionHeight + 30;
        relPos = Location - center;
        if ((relPos.X < area.X) && (relPos.X > -area.X) &&
            (relPos.Y < area.Y) && (relPos.Y > -area.Y))
        {
            // hack
            if (hitNorm.Y == 0)
                hitNorm.Y = 0.00000001;
            if (hitNorm.X == 0)
                hitNorm.X = 0.00000001;
            if (hitNorm.X > 0)
                distX = (area.X - relPos.X)/hitNorm.X;
            else
                distX = (-area.X - relPos.X)/hitNorm.X;
            if (hitNorm.Y > 0)
                distY = (area.Y - relPos.Y)/hitNorm.Y;
            else
                distY = (-area.Y - relPos.Y)/hitNorm.Y;
            dist = FMin(distX, distY);
            if (dist < 45)
                dist = 45;
            else if (dist > 700)
                dist = 700;  // sanity check
            if (!AIDirectionReachable(Location, rot.Yaw, rot.Pitch, 40, dist, destLoc))
                destLoc = Location;
        }
        else
            destLoc = Location;
    }

    function vector FocusDirection()
    {
        return (Vector(Rotation)*30+Location);
    }

    function StopWaiting()
    {
        GotoState('OpeningDoor', 'DoorOpened');
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        bCanJump = false;
        BlockReactions();
        bStasis = False;
        bInTransientState = True;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bAcceptBump = True;

        if (JumpZ > 0)
            bCanJump = true;

        ResetReactions();
        bStasis = True;
        bInTransientState = false;
    }

Begin:
    destLoc = vect(0,0,0);

BeginHitNormal:
    Acceleration = vect(0,0,0);
    FindBackupPoint();

    if (!DoorEncroaches())
        if (!FrobDoor(Target))
            Goto('DoorOpened'); 
    PlayRunning();
    StrafeTo(destLoc, FocusDirection());
    if (DoorEncroaches())
        if (!FrobDoor(Target))
            Goto('DoorOpened');
    PlayWaiting();
    Sleep(5.0);

DoorOpened:
    if (HasNextState())
        GotoNextState();
    else
        FollowOrders();  // THIS IS BAD!!!

}


// ----------------------------------------------------------------------
// state TakingHit
//
// React to a hit.
// ----------------------------------------------------------------------

state TakingHit
{
    ignores seeplayer, hearnoise, bump, hitwall, reacttoinjury;

    function TakeDamage(int Damage, Pawn instigatedBy, Vector hitlocation, 
                        Vector momentum, name damageType)
    {
        TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false);
    }

    function Landed(vector HitNormal)
    {
        if (Velocity.Z < -1.4 * JumpZ)
            MakeNoise(-0.5 * Velocity.Z/(FMax(JumpZ, 150.0)));
        bJustLanded = true;
    }

    function BeginState()
    {
        StandUp();
        LastPainTime = Level.TimeSeconds;
        LastPainAnim = AnimSequence;
        bInterruptState = false;
        BlockReactions();
        bCanConverse = False;
        bStasis = False;
        SetDistress(true);
        TakeHitTimer = 2.0;
        EnemyReadiness = 1.0;
        ReactionLevel  = 1.0;
        bInTransientState = true;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bInterruptState = true;
        ResetReactions();
        bCanConverse = True;
        bStasis = True;
        bInTransientState = false;
    }
        
Begin:
    Acceleration = vect(0, 0, 0);
    FinishAnim();
    if ( (Physics == PHYS_Falling) && !Region.Zone.bWaterZone )
    {
        Acceleration = vect(0,0,0);
        GotoState('FallingState', 'Ducking');
    }
    else if (HasNextState())
        GotoNextState();
    else
        GotoState('Wandering');
}


// ----------------------------------------------------------------------
// state RubbingEyes
//
// React to evil things like pepper spray.
// ----------------------------------------------------------------------

state RubbingEyes
{
    ignores seeplayer, hearnoise, bump, hitwall;

    function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
                        Vector momentum, name damageType)
    {
        TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false);
    }

    function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
    {
        if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned'))
            Global.ReactToInjury(instigatedBy, damageType, hitPos);
    }

    function SetFall()
    {
        StartFalling(NextState, NextLabel);
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
//		LastPainTime = Level.TimeSeconds;
//		LastPainAnim = AnimSequence;
        bInterruptState = false;
        BlockReactions();
        bCanConverse = False;
        bStasis = False;
        SetupWeapon(false, true);
        SetDistress(true);
        bStunned = True;
        bInTransientState = true;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bInterruptState = true;
        ResetReactions();
        bCanConverse = True;
        bStasis = True;
        if (Health > 0)
            bStunned = False;
        bInTransientState = false;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    PlayTearGasSound();

RubEyes:
    PlayRubbingEyesStart();
    FinishAnim();
    PlayRubbingEyes();
    Sleep(15);
    PlayRubbingEyesEnd();
    FinishAnim();
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Wandering');
}


// ----------------------------------------------------------------------
// state Stunned
//
// React to being stunned.
// ----------------------------------------------------------------------

state Stunned
{
    ignores seeplayer, hearnoise, bump, hitwall;

    function TakeDamage( int Damage, Pawn instigatedBy, Vector hitlocation, 
                        Vector momentum, name damageType)
    {
        TakeDamageBase(Damage, instigatedBy, hitlocation, momentum, damageType, false);
    }

    function ReactToInjury(Pawn instigatedBy, Name damageType, EHitLocation hitPos)
    {
        if ((damageType != 'TearGas') && (damageType != 'HalonGas') && (damageType != 'Stunned'))
            Global.ReactToInjury(instigatedBy, damageType, hitPos);
    }

    function SetFall()
    {
        StartFalling(NextState, NextLabel);
    }

    function AnimEnd()
    {
        PlayWaiting();
    }

    function BeginState()
    {
        StandUp();
        Disable('AnimEnd');
        bInterruptState = false;
        BlockReactions();
        bCanConverse = False;
        bStasis = False;
        SetupWeapon(false);
        SetDistress(true);
        bStunned = True;
        bInTransientState = true;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bInterruptState = true;
        ResetReactions();
        bCanConverse = True;
        bStasis = True;

        // if we're dead, don't reset the flag
        if (Health > 0)
            bStunned = False;
        bInTransientState = false;
    }

Begin:
    Acceleration = vect(0, 0, 0);
    PlayStunned();
    Sleep(15);
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Wandering');
}


// ----------------------------------------------------------------------
// state Dying
//
// Why does the Unreal Dying state suck?
// ----------------------------------------------------------------------

state Dying
{
    ignores SeePlayer, EnemyNotVisible, HearNoise, KilledBy, Trigger, Bump, HitWall, HeadZoneChange, FootZoneChange, ZoneChange, Falling, WarnTarget, Died, Timer, TakeDamage;

    event Landed(vector HitNormal)
    {
        SetPhysics(PHYS_Walking);
    }

    function Tick(float deltaSeconds)
    {
        Global.Tick(deltaSeconds);

        if (DeathTimer > 0)
        {
            DeathTimer -= deltaSeconds;
            if ((DeathTimer <= 0) && (Physics == PHYS_Walking))
                Acceleration = vect(0,0,0);
        }
    }

    function MoveFallingBody()
    {
        local Vector moveDir;
        local float  totalTime;
        local float  speed;
        local float  stopTime;
        local int    numFrames;

        if ((AnimRate > 0) && !IsA('Robot'))
        {
            totalTime = 1.0/AnimRate;  // determine how long the anim lasts
            numFrames = int((1.0/(1.0-AnimLast))+0.1);  // count frames (hack)

            // defaults
            moveDir   = vect(0,0,0);
            stopTime  = 0.01;

            ComputeFallDirection(totalTime, numFrames, moveDir, stopTime);

            speed = VSize(moveDir)/stopTime;  // compute speed

            // Set variables necessary for movement when walking
            if (moveDir == vect(0,0,0))
                Acceleration = vect(0,0,0);
            else
                Acceleration = Normal(moveDir)*AccelRate;
            GroundSpeed  = speed;
            DesiredSpeed = 1.0;
            bIsWalking   = false;
            DeathTimer   = stopTime;
        }
        else
            Acceleration = vect(0,0,0);
    }

    function BeginState()
    {
        EnableCheckDestLoc(false);
        StandUp();

        // don't do that stupid timer thing in Pawn.uc
        AIClearEventCallback('Futz');
        AIClearEventCallback('MegaFutz');
        AIClearEventCallback('Player');
        AIClearEventCallback('WeaponDrawn');
        AIClearEventCallback('LoudNoise');
        AIClearEventCallback('WeaponFire');
        AIClearEventCallback('Carcass');
        AIClearEventCallback('Distress');

        bInterruptState = false;
        BlockReactions(true);
        bCanConverse = False;
        bStasis = False;
        SetDistress(true);
        DeathTimer = 0;
    }

Begin:
    WaitForLanding();
    MoveFallingBody();

    DesiredRotation.Pitch = 0;
    DesiredRotation.Roll  = 0;

    // if we don't gib, then wait for the animation to finish
    if ((Health > -100) && !IsA('Robot'))
        FinishAnim();

    SetWeapon(None);

    bHidden = True;

    Acceleration = vect(0,0,0);
    SpawnCarcass();
    Destroy();
}


// ----------------------------------------------------------------------
// state FallingState
//
// Fall!
// ----------------------------------------------------------------------

state FallingState 
{
    ignores Bump, Hitwall, WarnTarget, ReactToInjury;

    function ZoneChange(ZoneInfo newZone)
    {
        Global.ZoneChange(newZone);
        if (newZone.bWaterZone)
            GotoState('FallingState', 'Splash');
    }

    //choose a jump velocity
    function AdjustJump()
    {
        local float velZ;
        local vector FullVel;

        velZ = Velocity.Z;
        FullVel = Normal(Velocity) * GroundSpeed;

        If (Location.Z > Destination.Z + CollisionHeight + 2 * MaxStepHeight)
        {
            Velocity = FullVel;
            Velocity.Z = velZ;
            Velocity = EAdjustJump();
            Velocity.Z = 0;
            if ( VSize(Velocity) < 0.9 * GroundSpeed )
            {
                Velocity.Z = velZ;
                return;
            }
        }

        Velocity = FullVel;
        Velocity.Z = JumpZ + velZ;
        Velocity = EAdjustJump();
    }

    singular function BaseChange()
    {
        local float minJumpZ;

        Global.BaseChange();

        if (Physics == PHYS_Walking)
        {
            minJumpZ = FMax(JumpZ, 150.0);
            bJustLanded = true;
            if (Health > 0)
            {
                if ((Velocity.Z < -0.8 * minJumpZ) || bUpAndOut)
                    GotoState('FallingState', 'Landed');
                else if (Velocity.Z < -0.8 * JumpZ)
                    GotoState('FallingState', 'FastLanded');
                else
                    GotoState('FallingState', 'Done');
            }
        }
    }

    function Landed(vector HitNormal)
    {
        local float landVol, minJumpZ;
        local vector legLocation;

        minJumpZ = FMax(JumpZ, 150.0);

        if ( (Velocity.Z < -0.8 * minJumpZ) || bUpAndOut)
        {
            PlayLanded(Velocity.Z);
            if (Velocity.Z < -700)
            {
                legLocation = Location + vect(-1,0,-1);         // damage left leg
                TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell');
                legLocation = Location + vect(1,0,-1);          // damage right leg
                TakeDamage(-0.14 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell');
                legLocation = Location + vect(0,0,1);           // damage torso
                TakeDamage(-0.04 * (Velocity.Z + 700), Self, legLocation, vect(0,0,0), 'fell');
            }
            landVol = Velocity.Z/JumpZ;
            landVol = 0.005 * Mass * FMin(5, landVol * landVol);
            if ( !FootRegion.Zone.bWaterZone )
                PlaySound(Land, SLOT_Interact, FMin(20, landVol));
        }
        else if ( Velocity.Z < -0.8 * JumpZ )
            PlayLanded(Velocity.Z);
    }

    function SetFall()
    {
        if (!bUpAndOut)
            GotoState('FallingState');
    }

    function BeginState()
    {
        StandUp();
        if (Enemy == None)
            Disable('EnemyNotVisible');
        else
        {
            Disable('HearNoise');
            Disable('SeePlayer');
        }
        bInterruptState = false;
        bCanConverse = False;
        bStasis = False;
        bInTransientState = true;
        EnableCheckDestLoc(false);
    }

    function EndState()
    {
        EnableCheckDestLoc(false);
        bUpAndOut = false;
        bInterruptState = true;
        bCanConverse = True;
        bStasis = True;
        bInTransientState = false;
    }

LongFall:
    if ( bCanFly )
    {
        SetPhysics(PHYS_Flying);
        Goto('Done');
    }
    Sleep(0.7);
    PlayFalling();
    if ( Velocity.Z > -150 ) //stuck
    {
        SetPhysics(PHYS_Falling);
        if ( Enemy != None )
            Velocity = groundspeed * normal(Enemy.Location - Location);
        else
            Velocity = groundspeed * VRand();

        Velocity.Z = FMax(JumpZ, 250);
    }
    Goto('LongFall');

FastLanded:
    FinishAnim();
    TweenToWaiting(0.15);
    Goto('Done');

Landed:
    if ( !bIsPlayer ) //bots act like players
        Acceleration = vect(0,0,0);
    FinishAnim();
    TweenToWaiting(0.2);
    if ( !bIsPlayer )
        Sleep(0.08);

Done:
    bUpAndOut = false;
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Wandering');

Splash:
    bUpAndOut = false;
    FinishAnim();
    if (HasNextState())
        GotoNextState();
    else
        GotoState('Wandering');

Begin:
    if (Enemy == None)
        Disable('EnemyNotVisible');
    else
    {
        Disable('HearNoise');
        Disable('SeePlayer');
    }
    if (bUpAndOut) //water jump
    {
        if ( !bIsPlayer ) 
        {
            DesiredRotation = Rotation;
            DesiredRotation.Pitch = 0;
            Velocity.Z = 440; 
        }
    }
    else
    {   
        if (Region.Zone.bWaterZone)
        {
            SetPhysics(PHYS_Swimming);
            GotoNextState();
        }   
        if ( !bJumpOffPawn )
            AdjustJump();
        else
            bJumpOffPawn = false;

PlayFall:
        PlayFalling();
        FinishAnim();
    }
    
    if (Physics != PHYS_Falling)
        Goto('Done');
    Sleep(2.0);
    Goto('LongFall');

Ducking:
        
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// OBSOLETE
// ----------------------------------------------------------------------

function PlayHit(float Damage, vector HitLocation, name damageType, vector Momentum)
{
    log("ERROR - PlayHit should not be called!");
}

function PlayHitAnim(vector HitLocation, float Damage)
{
    log("ERROR - PlayHitAnim should not be called!");
} 

function PlayDeathHit(float Damage, vector HitLocation, name damageType, vector Momentum)
{
    log("ERROR - PlayDeathHit should not be called!");
}

function PlayChallenge()
{
    log("ERROR - PlayChallenge should not be called!");
}

function JumpOffPawn()
{
    /*
    Velocity += (60 + CollisionRadius) * VRand();
    Velocity.Z = 180 + CollisionHeight;
    SetPhysics(PHYS_Falling);
    bJumpOffPawn = true;
    SetFall();
    */
    //log("ERROR - JumpOffPawn should not be called!");
}


// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

defaultproperties
{
     Restlessness=0.500000
     Wanderlust=0.500000
     Cowardice=0.500000
     maxRange=4000.000000
     MinHealth=30.000000
     RandomWandering=1.000000
     bAlliancesChanged=True
     Orders=Wandering
     HomeExtent=800.000000
     WalkingSpeed=0.400000
     bCanBleed=True
     ClotPeriod=30.000000
     bShowPain=True
     bCanSit=True
     bLikesNeutral=True
     bUseFirstSeatOnly=True
     bCanConverse=True
     bAvoidAim=True
     AvoidAccuracy=0.400000
     bAvoidHarm=True
     HarmAccuracy=0.800000
     CloseCombatMult=0.300000
     bHateShot=True
     bHateInjury=True
     bReactPresence=True
     bReactProjectiles=True
     bEmitDistress=True
     RaiseAlarm=RAISEALARM_BeforeFleeing
     bMustFaceTarget=True
     FireAngle=360.000000
     MaxProvocations=1
     AgitationSustainTime=30.000000
     AgitationDecayRate=0.050000
     FearSustainTime=25.000000
     FearDecayRate=0.500000
     SurprisePeriod=2.000000
     SightPercentage=0.500000
     bHasShadow=True
     ShadowScale=1.000000
     BaseAssHeight=-26.000000
     EnemyTimeout=4.000000
     bTickVisibleOnly=True
     bInWorld=True
     bHighlight=True
     bHokeyPokey=True
     InitialInventory(0)=(Count=1)
     InitialInventory(1)=(Count=1)
     InitialInventory(2)=(Count=1)
     InitialInventory(3)=(Count=1)
     InitialInventory(4)=(Count=1)
     InitialInventory(5)=(Count=1)
     InitialInventory(6)=(Count=1)
     InitialInventory(7)=(Count=1)
     bSpawnBubbles=True
     bWalkAround=True
     BurnPeriod=30.000000
     DistressTimer=-1.000000
     CloakThreshold=50
     walkAnimMult=0.700000
     runAnimMult=1.000000
     bCanStrafe=True
     bCanWalk=True
     bCanSwim=True
     bCanOpenDoors=True
     bIsHuman=True
     bCanGlide=False
     AirSpeed=320.000000
     AccelRate=200.000000
     JumpZ=120.000000
     MinHitWall=9999999827968.000000
     HearingThreshold=0.150000
     Skill=2.000000
     AIHorizontalFov=160.000000
     AspectRatio=2.300000
     bForceStasis=True
     BindName="ScriptedPawn"
     FamiliarName="DEFAULT FAMILIAR NAME - REPORT THIS AS A BUG"
     UnfamiliarName="DEFAULT UNFAMILIAR NAME - REPORT THIS AS A BUG"
}

Overview Package Class Source Class tree Glossary
previous class      next class frames      no frames
Class file time: Mon 8/11/2021 16:22:50.000 - Creation time: Mon 8/11/2021 16:31:32.275 - Created with UnCodeX