File: actor.lua

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (1685 lines) | stat: -rw-r--r-- 51,103 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
--io.stderr:write("actor.lua get here\n")

--Worktypes
WT_NOTHING                = 0x0  --do nothing

WT_FOLLOW                 = 0x1  --follow avatar (in party)

WT_PLAYER                 = 0x2  --player mode

WT_SLEEP                  = 0x91

ACTOR_STAT_DMG = 7
--Actor stats table
--[obj_num] = {str,dex,int,hp,alignment,,damage??,,,,,,,,,,,,,,,,}
actor_tbl = {
--OBJ_YOURSELF5
[343] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_YOURSELF6
[344] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_POOR_MONK
[342] = {22,22,22,60,ALIGNMENT_EVIL,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_SCIENTIST
[345] = {15,15,25,30,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_YOUNG_WOMAN
[346] = {18,18,22,60,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_COWBOY
[347] = {20,25,18,60,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_GENTLEMAN
[348] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_UNIFORMED_MAN
[349] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_ADVENTURER
[350] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_COMMON_FELLOW
[351] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_MAN
[352] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_MAN_IN_WHITE
[353] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_LADY
[354] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_WOMAN
[355] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_ADVENTURESS
[356] = {25,25,25,90,ALIGNMENT_GOOD,0,6,0,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_METAL_WOMAN1
[357] = {25,25,25,90,ALIGNMENT_GOOD,0,10,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_MECHANICAL_MAN
[358] = {25,15,10,40,ALIGNMENT_GOOD,4,12,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_MARTIAN
[369] = {15,15,20,30,ALIGNMENT_NEUTRAL,0,6,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_SEXTELLEGER
[359] = {20,20,10,45,ALIGNMENT_CHAOTIC,4,35,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_BUSHALO
[360] = {25,12,11,35,ALIGNMENT_NEUTRAL,8,20,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
--OBJ_PLANTELLOPE
[361] = {15,22,9,20,ALIGNMENT_NEUTRAL,1,12,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_ROCKWORM2
[372] = {20,15,10,40,ALIGNMENT_CHAOTIC,8,25,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_WISP
[377] = {20,20,20,80,ALIGNMENT_NEUTRAL,3,30,0,1,0,1,0,1,0,0,0,0,0,0,0,0,0,0},
--OBJ_AIRSQUID
[378] = {10,20,15,10,ALIGNMENT_EVIL,0,6,0,0,0,0,1,0,0,1,1,0,0,0,0,0,0,0},
--OBJ_AIRSQUID1
[380] = {15,15,8,30,ALIGNMENT_EVIL,4,20,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_GIANT_MAW
[373] = {30,10,9,100,ALIGNMENT_CHAOTIC,3,30,1,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_TENTACLE1
[362] = {20,20,9,10,ALIGNMENT_CHAOTIC,2,20,1,1,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
--OBJ_CREEPING_CACTUS
[383] = {20,12,3,30,ALIGNMENT_CHAOTIC,4,25,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_AMMONOID
[363] = {15,15,3,20,ALIGNMENT_CHAOTIC,10,12,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_PROTO_MARTIAN
[364] = {20,19,10,20,ALIGNMENT_EVIL,2,10,0,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
--OBJ_MONSTER_FOOTPRINTS
[145] = {20,19,10,20,ALIGNMENT_EVIL,2,12,1,0,1,0,0,0,0,0,1,0,1,0,0,0,0,0},
--OBJ_POD_DEVIL
[384] = {15,18,3,25,ALIGNMENT_CHAOTIC,4,12,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_JUMPING_BEAN
[385] = {10,22,10,10,ALIGNMENT_CHAOTIC,1,4,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_HEDGEHOG
[365] = {17,12,8,20,ALIGNMENT_CHAOTIC,3,20,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_CREEPER
[374] = {15,15,3,20,ALIGNMENT_CHAOTIC,1,16,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_AGROBOT
[376] = {29,18,10,30,ALIGNMENT_CHAOTIC,10,20,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
--OBJ_CANAL_WORM
[379] = {20,20,15,30,ALIGNMENT_CHAOTIC,5,25,0,0,0,1,0,0,1,0,1,0,0,0,0,0,0,0},
--OBJ_DUST_DEVIL
[381] = {30,30,30,255,ALIGNMENT_NEUTRAL,99,60,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0},
--OBJ_SAND_TRAPPER
[386] = {25,25,10,40,ALIGNMENT_CHAOTIC,4,18,1,0,1,1,0,0,0,0,1,0,0,0,0,1,0,0},
--OBJ_OXY_LEECH
[375] = {10,25,12,20,ALIGNMENT_CHAOTIC,1,10,0,1,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_CAVE_WORM
[366] = {15,15,15,30,ALIGNMENT_EVIL,2,15,0,1,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_JANITOR
[367] = {20,15,10,30,ALIGNMENT_CHAOTIC,10,20,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
--OBJ_BUSHRAT
[387] = {10,25,10,20,ALIGNMENT_EVIL,2,12,0,0,1,1,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_PLANTHER
[368] = {15,22,12,20,ALIGNMENT_CHAOTIC,4,20,0,0,1,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_MINOTAUR
[398] = {20,18,8,90,ALIGNMENT_NEUTRAL,2,15,0,1,1,1,0,0,0,0,0,0,1,0,0,0,0,0},
--OBJ_YOUR_MOTHER
[391] = {12,20,15,255,ALIGNMENT_CHAOTIC,9,1,0,1,0,1,0,0,0,0,0,0,1,0,0,0,0,0},
--OBJ_TREE
[408] = {25,18,5,90,ALIGNMENT_NEUTRAL,3,12,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_SOMETHING_YOU_SHOULDNT2
[424] = {25,18,5,90,ALIGNMENT_NEUTRAL,3,12,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
--OBJ_HUGE_RED_SPOT
[403] = {20,20,10,255,ALIGNMENT_EVIL,99,12,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}
}

function  is_actor_stat_bit_set(obj_n, bit_number)
   local stats = actor_tbl[obj_n]
   if stats ~= nil then
      if stats[23 - bit_number] == 1 then
         return true
      end
   end

   return false
end

g_party_is_warm = false

-- Berry logic
function actor_is_affected_by_purple_berries(actor_num)

   if actor_num < 16 and timer_get(actor_num*3) > 0 then
      return true
   end

   return false
end

function actor_is_affected_by_green_berries(actor_num)

   if actor_num < 16 and timer_get(actor_num*3+1) > 0 then
      return true
   end

   return false
end

function actor_is_affected_by_brown_berries(actor_num)

   if actor_num < 16 and timer_get(actor_num*3+2) > 0 then
      return true
   end

   return false
end

function actor_get_purple_berry_count(actor_num)
   if actor_num < 16 then
      return timer_get(actor_num*3)
   end

   return 0
end

function actor_get_green_berry_count(actor_num)
   if actor_num < 16 then
      return timer_get(actor_num*3+1)
   end

   return 0
end

function actor_get_brown_berry_count(actor_num)
   if actor_num < 16 then
      return timer_get(actor_num*3+2)
   end

   return 0
end

function actor_decrement_berry_counter(actor, berry_type)
   local actor_num = actor.actor_num
   local count = timer_get(actor_num*3+berry_type)
   if count > 0 and math.random(1, actor_int_adj(actor) * 4) == 1 then
      timer_set(actor_num*3+berry_type, count - 1)
      if (actor_num == 0 and g_in_dream_mode) or
         (actor_num ~= 0 and g_in_dream_mode == false) then
         play_md_sfx(0x32)
         printl("A_PSYCHIC_POWER_FADES")
      end
   end
end

function actor_decrement_berry_counters(actor)
   actor_decrement_berry_counter(actor, 0) --purple
   actor_decrement_berry_counter(actor, 1) --green
   actor_decrement_berry_counter(actor, 2) --brown
end

function actor_increment_berry_counter(actor_num, berry_type)
   local count = timer_get(actor_num*3+berry_type)
   if actor_num < 16 and count < 10 then
      timer_set(actor_num*3+berry_type, count + 1)
   end
end

function actor_increment_purple_berry_count(actor_num)
   actor_increment_berry_counter(actor_num, 0)
end

function actor_increment_green_berry_count(actor_num)
   actor_increment_berry_counter(actor_num, 1)
end

function actor_increment_brown_berry_count(actor_num)
   actor_increment_berry_counter(actor_num, 2)
end

function actor_get_blue_berry_counter()
   return timer_get(16*3)
end

function actor_set_blue_berry_counter(new_value)
   timer_set(16*3, new_value)
end

function actor_clear_berry_counters(actor_num)
   timer_set(actor_num*3, 0)
   timer_set(actor_num*3+1, 0)
   timer_set(actor_num*3+2, 0)
end

function actor_get_damage(actor)
   local stat = actor_tbl[actor]
   if stat ~= nil then
      return stat[ACTOR_STAT_DMG]
   end

   return nil
end

--
-- actor_init(actor)
--

function actor_init(actor, alignment)

 local actor_base = actor_tbl[actor.obj_n]
 if actor_base ~= nil then
   actor.str = actor_randomise_stat(actor_base[1])
   actor.dex = actor_randomise_stat(actor_base[2])
   actor.int = actor_randomise_stat(actor_base[3])
   actor.hp = actor_randomise_stat(actor_base[4])

   actor.level = 0
   actor.align = actor_base[5]
 else
   actor.str = 15
   actor.dex = 15
   actor.int = 15
   actor.hp = 30
   actor.level = 1
   actor.align = ALIGNMENT_CHAOTIC
 end

 if alignment ~= nil and alignment ~= ALIGNMENT_DEFAULT then
   actor.align = alignment
 end

 actor.wt = 8
 actor.combat_mode = 8
 actor.mpts = actor.dex
 actor.exp = 0

end


function subtract_map_movement_pts(actor)
   local points = map_get_impedence(actor.x, actor.y, actor.z, false) + 5
   subtract_movement_pts(actor, points)
end

function actor_divide(actor)

   local random = math.random
   local from_x, from_y, from_z = actor.x, actor.y, actor.z

   for i=1,8 do

      local new_x = wrap_coord(random(1, 2) + from_x - 1, from_z)
      local new_y = wrap_coord(random(1, 2) + from_y - 1, from_z)

      if map_can_put(new_x, new_y, from_z) then

         --FIXME need to call sub_2C657 which I think updates the existing actor
         Actor.clone(actor, new_x, new_y, from_z)
         printfl("ACTOR_DIVIDES", actor.name)
         return
      end
   end
end

function defender_update_alignment(attacker, defender, damage)
   if defender.luatype ~= "actor" then
      return
   end

   if defender.align == ALIGNMENT_NEUTRAL and defender.in_party == false and (defender.wt <= 0x10 or defender.wt >= 0x80) then
      if attacker.wt == WT_PLAYER and is_actor_stat_bit_set(defender.obj_n, 14) and damage > 0 then
         printfl("ACTOR_ATTACKS", defender.name)
      end

      if defender.actor_num >= 0xc0 or defender.wt < 0x80 then
         if attacker.align == ALIGNMENT_GOOD and defender.align ~= ALIGNMENT_EVIL and defender.wt ~= ALIGNMENT_CHAOTIC then
            defender.align = ALIGNMENT_EVIL
         end

         if defender.wt >= 0xc0 then
            defender.wt = 8
         end
      else
         if attacker.wt == WT_PLAYER then
            defender.wt = 0xa0
         end
      end
   end
end

function canal_worm_eat_body(worm_actor)
   --FIXME
   --only eat if a body was created when the actor was killed.
end

function actor_handle_damage(defender)
   if defender.obj_n == 370 or defender.hp == 0 then --OBJ_ROCKWORM_BASE
      return
   end

   local hp_level = math.floor(((defender.hp * 4) / defender.max_hp))
   if hp_level == 0 then
      printfl("ACTOR_CRITICAL", defender.name)
      --FIXME giant_maw check
      local wt = defender.wt
      if wt == 0xd then
         defender.wt = 0x11
         --FIXME if actor qaul ~= 0 then set actor.get(qual).wt = 0x11
      elseif wt ~= 6 and wt ~= 0xe and wt ~= 2 and wt ~= 1 and wt ~= 0 and wt ~= 0x11 and wt ~= 0x19
            and ( is_actor_stat_bit_set(defender.obj_n, 7) or is_actor_stat_bit_set(defender.obj_n, 14) ) then
         if actor_int_adj(defender) >= 5 and defender.obj_n ~= 145 then --OBJ_MONSTER_FOOTPRINTS
            defender.wt = 7
         end
      end
   elseif hp_level < 4 then
      local suffix
      if is_actor_stat_bit_set(defender.obj_n, 14) and is_actor_stat_bit_set(defender.obj_n, 7) then
         suffix = i18n("DAMAGED")
      else
         suffix = i18n("WOUNDED")
      end

      local damage_type
      if hp_level == 1 then
         damage_type = i18n("HEAVILY")
      elseif hp_level == 2 then
         damage_type = i18n("LIGHTLY")
      elseif hp_level == 3 then
         damage_type = i18n("BARELY")
      end
      printfl("ACTOR_HIT_MESSAGE", defender.name, damage_type, suffix)

   end
end

function actor_take_hit(attacker, defender, max_dmg, damage_mode)
   if max_dmg == -1 then
      max_dmg = 1
   elseif max_dmg > 1 and max_dmg < 255 then
      max_dmg = math.random(1, max_dmg)
   end

   local armour_value = 0
   if damage_mode ~= 2 then
      if defender.luatype == "actor" then
         armour_value = actor_get_ac(defender)
      elseif defender.obj_n == 388 and defender.frame_n == 0 then --OBJ_GLOW_WORM
         armour_value = 2
      end
   end

   if armour_value > 0 and max_dmg < 255 then
      max_dmg = max_dmg - math.random(1, armour_value)
   end

   if max_dmg > 0 then
      if (attacker.align == ALIGNMENT_EVIL or attacker.align == ALIGNMENT_CHAOTIC)
              and defender.luatype == "actor" and defender.in_party
              and party_is_in_combat_mode() == false
              and player_is_in_solo_mode() == false then
         party_set_combat_mode(true)
      end

      attacker.exp = attacker.exp + actor_hit(defender, max_dmg, damage_mode)
   else
      --FIXME call either sub_19088 or sub_18F7D
      printfl("ACTOR_GRAZED", defender.name)
   end

   if defender.luatype == "actor" then
      --FIXME only call if defender == selected obj
      defender_update_alignment(attacker, defender, max_dmg)
      if defender.hp == 0 then
         if attacker.obj_n == 379 then --OBJ_CANAL_WORM
            canal_worm_eat_body(attacker)
         end
      else
         if is_actor_stat_bit_set(attacker.obj_n, 2) and math.random(0, 3) == 0 and actor_immune_to_dmg(defender) == false then
            printfl("ACTOR_PARALYZED", defender.name)
            defender.paralyzed = true
         end

         if defender.alive and max_dmg > 0 then
            actor_handle_damage(defender)
         end
      end
   end

end

function actor_immune_to_dmg(actor)
   local actor_num = actor.actor_num
   local obj_n = actor.obj_n
   if obj_n == 381 or obj_n == 390 or obj_n == 391 or obj_n == 358 or obj_n == 382 then
      return true
   end

   if actor_num == 0x5c or actor_num == 0x5d or actor_num == 0x59 or actor_num == 0x5a or actor_num == 0x54 or actor_num == 0x52
      or actor_num == 0x6d or actor_num == 0x68 or actor_num == 0x67 or actor_num == 0x69 or actor_num == 0x77 or actor_num == 0x78
      or actor_num == 0x40 or actor_num == 0x3c then
      return true
   end

   return false
end

function actor_print_custom_hit_message(actor)
   local actor_num = actor.actor_num
   if actor_num == 0x69 then
      printfl("ACTOR_CRITICAL", Actor.get(0x19).name)
   elseif actor_num == 0x3c then
      printfl("ACTOR_CRITICAL", actor.name)
   elseif actor_num == 0x52 then
      printfl("ACTOR_HIT_MESSAGE", actor.name, i18n("HEAVILY"), i18n("WOUNDED"))
   elseif actor_num == 0x68 then
      if Actor.get_talk_flag(actor, 2) then
         printfl("ACTOR_CRITICAL", actor.name)
      else
         printfl("ACTOR_HIT_MESSAGE", actor.name, i18n("HEAVILY"), i18n("WOUNDED"))
      end
      Actor.set_talk_flag(actor, 5)
      Actor.talk(actor)
   else
      printl("IT_HAS_NO_EFFECT")
   end
end

local RED_HIT_TILE = 257
local BLUE_HIT_TILE = 258

function hit_target(target, hit_tile)
   if map_is_on_screen(target.xyz) then
      if target.luatype == "actor" and target.in_party then
         play_md_sfx(1, PLAY_ASYNC)
      else
         play_md_sfx(2, PLAY_ASYNC)
      end
      hit_anim(target.x, target.y) --FIXME need to apply hit colour tile param here.
   end

   if target.luatype == "actor" then
      --FIXME
--      les     bx, objlist_unk_1af1_ptr
--      mov     al, es:[bx+si]
--      or      al, 8
--      mov     es:[bx+si], al

      if target.asleep then
         target.asleep = false
      end
   end

end

-- Hit an actor or object
function actor_hit(defender, max_dmg, damage_mode)


   local defender_obj_n = defender.obj_n
   local exp_gained = 0
   local player_loc = player_get_location()

   if defender.z ~= player_loc.z or max_dmg < 0 then
      return 0
   end

   if defender.luatype == "actor" then
      print("actor_hit("..defender.actor_num..")\n")

      if defender.actor_num == 0 and defender.hp <= max_dmg and g_current_dream_stage == 0xe0 then
        max_dmg = defender.hp - 1
      end

      if damage_mode == 1 then
         hit_target(defender, BLUE_HIT_TILE)
      else
         hit_target(defender, RED_HIT_TILE)
      end

      if actor_immune_to_dmg(defender) then
         actor_print_custom_hit_message(defender)
         defender.hp = 0xff
         if defender_obj_n == 391 then --OBJ_YOUR_MOTHER
            local gender_title = ""
            if player_get_gender() == 0 then
               gender_title = i18n("MAN")
            else
               gender_title = i18n("WOMAN")
            end
            printfl("HOW_DARE_YOU_YOUNG_PERSON", gender_title)
            actor_take_hit(defender, Actor.get(0), 45, 0)
            actor_take_hit(defender, Actor.get(0), 45, 0)
            actor_take_hit(defender, Actor.get(0), 45, 0)
         elseif defender.actor_num == 0x6d or defender.actor_num == 0x67 then
            Actor.set_talk_flag(Actor.get(0x6b), 3)
            local dream_actor = Actor.get(0)
            Actor.move(dream_actor, g_prev_player_x, g_prev_player_y, dream_actor.z)
         elseif defender.actor_num == 0x40 then --Rasputin
            --FIXME attack_rasputin()
         end
      else
         if damage_mode == 3 and is_actor_stat_bit_set(defender_obj_n, 3) then
            printl("IT_HAS_NO_EFFECT")
            return 0
         end
         if is_actor_stat_bit_set(defender_obj_n, 4) then
            max_dmg = max_dmg * 2
         end

         if (damage_mode == 0 and actor_num ~= 0) or g_current_dream_stage ~= 0xe0 then --or word_41184 ~= 0xe0
            if math.random(6, 0x64) <= max_dmg then
               --FIXME actor_add_blood()
            end
            local hp = defender.hp
            if hp <= max_dmg then
               --FIXME exp_gained = kill_actor()
            else
               defender.hp = hp - max_dmg

               if defender.wt == 9 or defender.wt == 11 then
                  defender.wt = 8
               elseif defender.wt == 10 or defender.wt == 12 then
                  defender.wt = 0x1b
               end

               if is_actor_stat_bit_set(defender_obj_n, 6) then
                  actor_divide(defender)
               end
            end
         end
      end
   else
      actor_hit_obj(defender, max_dmg, damage_mode)
   end

   return exp_gained
end

function attack_bucket(bucket, damage_mode)
   if damage_mode == 3 and bucket.frame_n == 2 then
      printl("THE_ICE_THAWS")
      bucket.frame_n = 1
   elseif damage_mode == 1 and bucket.frame_n == 1 then
      printl("THE_WATER_FREEZES")
      bucket.frame_n = 2
   end
end

function actor_hit_obj(obj, dmg, damage_mode)
   if obj.qty == 0 or not is_obj_attackable(obj) then
      if obj.obj_n == 160 then --OBJ_BUCKET
         attack_bucket(obj, damage_mode)
      end
      return
   end

   if damage_mode == 0 or damage_mode == 3 or is_plant_obj(obj) then

      if damage_mode == 1 then
         hit_target(obj, BLUE_HIT_TILE)
      else
         hit_target(obj, RED_HIT_TILE)
      end

      if damage_mode == 3 and is_obj_burnable(obj) then
         dmg = obj.qty
      end

      if obj.qty <= dmg then
         if obj.obj_n == 217 then --OBJ_GLASS_PITCHER
            play_md_sfx(0x1a)
            obj.obj_n = 218 --OBJ_BROKEN_CRYSTAL
            obj.frame_n = 0
            if g_in_dream_mode and g_current_dream_stage == 0x44 then
               local pitcher
               for tmp_obj in find_obj_from_area(0x21, 0x33, 2, 0x1c, 0x10) do
                  if tmp_obj.obj_n == 217 then --OBJ_GLASS_PITCHER
                     pitcher = tmp_obj
                  end
               end
               if pitcher == nil then
                  Actor.set_talk_flag(0x54, 6)
               end
            end
         else
            play_md_sfx(0)
            Obj.removeFromEngine(obj)
         end
      else
         if obj.obj_n == 307 then --OBJ_DEVIL_POD
            printl("THE_POD_SPLITS_OPEN")
            local pod_devil = Actor.new(384, obj.x, obj.y, obj.z)

            actor_init(pod_devil)
            Actor.move(pod_devil, obj.x, obj.y, obj.z)
            Obj.removeFromEngine(obj)
         else
            if obj.obj_n == 388 then --OBJ_GLOW_WORM
               obj.frame_n = 0
               obj.quality = math.random(0xb, 0x12)
               play_md_sfx(0x22)
            end
            obj.qty = obj.qty - dmg
         end

      end

   end

end

function actor_move(actor, direction, flag)
   ----dgb("actor_move("..actor.name..", "..direction_string(direction)..", "..flag..") actor("..actor.x..","..actor.y..")\n");
   local x,y,z = actor.x, actor.y, actor.z
   if direction == DIR_NORTH then y = y - 1 end
   if direction == DIR_SOUTH then y = y + 1 end
   if direction == DIR_EAST then x = x + 1 end
   if direction == DIR_WEST then x = x - 1 end

   if actor.obj_n ~= 382 then --COKER FIXME need to have an update frame function here.
      actor.direction = direction
   end

   local did_move = Actor.move(actor, x, y, z)

   --FIXME need more logic here.
   --footprints, bots etc.

   if did_move then
      subtract_map_movement_pts(actor)
      ----dgb("actor_move() did move actor("..actor.x..","..actor.y..")\n");
   end

   return did_move
end

function actor_move_diagonal(actor, x_direction, y_direction)
   local x,y,z = actor.x, actor.y, actor.z
   local direction

   if y_direction == DIR_NORTH then
      y = y - 1
      direction = x_direction == DIR_EAST and DIR_NORTHEAST or DIR_NORTHWEST
   end
   if y_direction == DIR_SOUTH then
      y = y + 1
      direction = x_direction == DIR_EAST and DIR_SOUTHEAST or DIR_SOUTHWEST
   end
   if x_direction == DIR_EAST then
      x = x + 1
      direction = y_direction == DIR_NORTH and DIR_NORTHEAST or DIR_SOUTHEAST
   end
   if x_direction == DIR_WEST then
      x = x - 1
      direction = y_direction == DIR_NORTH and DIR_NORTHWEST or DIR_SOUTHWEST
   end

   ----dgb("actor_move_diagonal("..actor.name..", "..direction_string(direction)..")\n");
   actor.direction = y_direction
   local did_move = Actor.move(actor, x, y, z)

   if did_move then
      subtract_map_movement_pts(actor)
   end

   return did_move and 1 or 0
end

function actor_update_frame(actor, direction)
   local obj_n = actor.obj_n

   --actor.direction = direction
   if obj_n >= 342 or obj_n <= 358 then --human actors
      --print("actor: "..actor.actor_num.."("..actor.x..","..actor.y..","..actor.z..")")
      for obj in objs_at_loc(actor.x, actor.y, actor.z) do
         if obj ~= nil then
            local tmp_obj_n = obj.obj_n
            --print("actor:"..actor.actor_num.." obj: " .. tmp_obj_n)
            if tmp_obj_n == 406 or (tmp_obj_n == 407) or tmp_obj_n == 216 or tmp_obj_n == 289 then
               --sit here
               if tmp_obj_n ==  406 then -- OBJ_BENCH
                  actor.frame_n = 3
               elseif tmp_obj_n == 407 then -- OBJ_COUCH
                  actor.frame_n = 4 + 3
               elseif tmp_obj_n == 289 then -- OBJ_DREAM_MACHINE2
                  actor.frame_n = 8 + 3
               else
                  actor.frame_n = math.floor(direction/2) * 4 + 3
               end

               return
            end
         end
      end
      --standing

   end
end

function revive_avatar()
   if g_in_dream_mode == true then
      --FIXME died in dream world. old_player_x == 0
      --FIXME respawn into dreamworld
   else
      -- normal avatar death
      avatar_falls_unconscious()
   end
end

function actor_resurrect(actor)
   --FIXME do we need to do anything here?
end

function avatar_falls_unconscious()
   printl("OVERCOME_BY_YOUR_WOUNDS_YOU_FALL_UNCONSCIOUS")

   fade_out()

   local input
   while input == nil do
      --canvas_update()
      input = input_poll()
      if input ~= nil then
         break
      end
   end

   local location
   local target
   if Actor.get_talk_flag(0x46, 3) then
      location = i18n("THE_OLYMPUS_MINE")
      target={x=0xa4,y=0xc3,z=4}
   else
      location = i18n("THE_SPACE_CAPSULE")
      target={x=0x19d,y=0x278,z=0}
   end

   printfl("YOU_AWAKEN_BACK_AT_FEELING_RESTORED", location)

   input_select(nil, true)

   party_resurrect_dead_members()

   for actor in party_members() do
      actor.hp = actor.max_hp
      actor.mpts = actor_dex_adj(actor)
   end

   party_move(target)
   local hour = clock_get_hour()
   local minutes = clock_get_minute()
   minutes = 60 - minutes
   if hour < 7 then
      hour = 6 - hour
   else
      hour = 24 - (hour + 1) + 7
   end

   clock_inc(hour * 60 + minutes + math.random(0,59)) --advance time to between 7am and 8am on the next day
   update_watch_tile()
   advance_time(0)

   party_update_leader()
   party_set_combat_mode(false)
   party_set_party_mode()

   local blood = Actor.get(0x12)
   if blood.alive then
      Actor.set_talk_flag(blood, 5)
      Actor.talk(blood)
   end

   fade_in()
end

function party_update()
   local avatar = Actor.get(1)
   if avatar.hp == 0 or (g_in_dream_mode == true and Actor.get(0).alive == false) then
      if g_in_dream_mode == true then
         g_objlist_1d22_unk = 0
         if g_prev_player_x == 0 then
            printl("YOU_SHAKE_YOURSELF_AWAKE_FROM_THE_NIGHTMARE")
            wake_from_dream()
         else
            local avatar = Actor.get(1)
            local dream_actor = Actor.get(0)
            dream_actor.hp = avatar.max_hp
            dream_actor.poisoned = false
            Actor.move(dream_actor, g_prev_player_x, g_prev_player_y, dream_actor.z)
            party_set_combat_mode(false)
            printl("YOU_FEEL_YOUR_DREAM_CONSCIOUSNESS_RETURNING")
            if g_current_dream_stage == 0xc0 then
               if not Actor.get_talk_flag(0x66, 2)
                  and Actor.get_talk_flag(0x66, 3)
                  and Actor.get_talk_flag(0x66, 4)
                  and not Actor.get_talk_flag(0x66, 5)
               then
                  for i=0,0xff do
                     local maw = Actor.get(i)
                     if maw.obj_n == 373 and maw.wt == 0x12 and maw.x == 0x87 and maw.y == 0x17 then
                        Actor.kill(maw, false)
                     end
                  end
                  if map_get_obj(0x7f, 0x18, dream_actor.z, 224) == nil then --OBJ_BRIDGE
                     local bridge = Obj.new(224, 3)
                     Obj.moveToMap(bridge, 0x7f, 0x18, dream_actor.z)
                  end
               end
               Actor.set_talk_flag(0x66, 7)
               local raxachk = Actor.get(0x66)
               Actor.talk(raxachk)
            end
         end
      else
         revive_avatar()
      end
   end

end

function actor_update_all()
   party_update()
   --pathfinding here.

   local actor
   local selected_actor
   repeat
      selected_actor = nil
      local di = 0
      local dex_6 = 1
      repeat
         local player_loc = player_get_location()
         local var_C = (player_loc.x - 16) - (player_loc.x - 16) % 8
         local var_A = (player_loc.y - 16) - (player_loc.y - 16) % 8

         for actor in party_members() do
            if actor.wt == WT_FOLLOW and actor.mpts < 0 then
               actor.mpts = 0
            end
         end

         local player_z = player_loc.z
         for i=0,0xff do
            local actor = Actor.get(i)
            --FIXME need to check 11000b not zero on obj_flags
            if actor.obj_n ~= 0 and actor.z == player_z and actor.mpts > 0 and actor.paralyzed == false and (actor.asleep == false or actor.wt == 0x80) and actor.wt ~= WT_NOTHING and actor.alive == true then
                  --FIXME need to check map wrapping here.
                  if abs(actor.x - var_C) > 0x27 or abs(actor.y - var_A) > 0x27 then
                     if actor.wt >= 0x83 and actor.wt <= 0x86 then
                        --move actor to schedule location if it isn't on screen
                        local sched_loc = actor.sched_loc
                        if map_is_on_screen(sched_loc.x, sched_loc.y, sched_loc.z) == false then
                           Actor.move(actor, sched_loc.x, sched_loc.y, sched_loc.z)
                           actor_wt_walk_to_location(actor) --this will cancel the pathfinder and set the new worktype
                           subtract_movement_pts(actor, 10)
                           ----dgb("\nActor SCHEDULE TELEPORT "..actor.actor_num.." to ("..sched_loc.x..","..sched_loc.y..","..sched_loc.z..")\n")
                        end
                     end
                  else
                     if actor.wt ~= WT_FOLLOW then
                        if actor.wt == 0x80 then
                           -- actor_set_worktype_from_schedule(actor)
                           actor.wt = actor.sched_wt
                        end

                        local dex_adjusted = actor_dex_adj(actor)
                        local dx = (actor.mpts * dex_6) - dex_adjusted * di
                        if actor.mpts >= dex_adjusted or dx > 0 or dx == 0 and dex_adjusted > dex_6 then
                           selected_actor = actor
                           di = actor.mpts
                           dex_6 = dex_adjusted
                        end

                        if dex_adjusted <= actor.mpts then
                           break
                        end
                     end
                  end
            end
         end

         if di <= 0 then
            for i=0,0xff do
               local actor = Actor.get(i)
               local dex_adjusted = actor_dex_adj(actor)
               if actor.mpts >= 0 then
                  actor.mpts = dex_adjusted
               else
                  actor.mpts = actor.mpts + dex_adjusted
               end
            end
            advance_time(1)
         end

      until di > 0

      if selected_actor.wt ~= WT_PLAYER and selected_actor.wt ~= WT_FOLLOW then
         --print("perform_worktype("..selected_actor.name.."("..selected_actor.actor_num..") dex = "..selected_actor.dex.." mpts = "..selected_actor.mpts..").\n")
         perform_worktype(selected_actor)
         party_update()
         if selected_actor.wt > 1 and selected_actor.wt < 0x1b then
            --FIXME targeting?? do *(&bjlist_unk_19f1_ptr + actor_num) = actor_num
         end
      end

   until selected_actor.obj_n ~= 0 and selected_actor.wt == WT_PLAYER

   if selected_actor ~= nil then --swap player to next party member with 'command' combat worktype.
    local old_player = Actor.get_player_actor()
    player_set_actor(selected_actor)
    old_player.wt = WT_PLAYER --reset worktype to player as it gets changed to follow in Player::set_actor() :-(
   end


   display_prompt(true)
end



local projectile_weapon_tbl = --FIXME weed sprayer and spray gun
{
--obj_n = {tile_num, initial_tile_rotation, speed, rotation_amount} --FIXME: all untested
[40] = {575, 90,4, 0}, -- Cupid's bow and arrows
[41] = {259, 90,4, 0}, -- derringer --FIXME: tile_num, rot, speed, amount
[42] = {259, 90,4, 0}, -- revolver --FIXME: rot, speed, amount
[43] = {262, 90,4, 0}, -- shotgun --FIXME: rot, speed, amount
[44] = {259, 90,4, 0}, -- rifle --FIXME: rot, speed, amount
[45] = {259, 90,4, 0}, -- Belgian combine rifle --FIXME: rot, speed, amount (has 3 modes)
--[45] = {262, 90,4, 0}, -- Belgian combine spread(has two) --FIXME: rot, speed, amount
[46] = {259, 90,4, 0}, -- elephant gun --FIXME: tile_num, rot, speed, amount
[47] = {398, 0, 2, 0}, -- sling --FIXME: tile_num, rot, speed, amount
[48] = {575, 90,4, 0}, -- bow
[241] = {334, 90,4, 0}, -- heat ray gun --FIXME: rot, speed, amount (has 3 modes)
[242] = {335, 90,4, 0}, -- freeze ray gun --FIXME: rot, speed, amount (has 3 modes)
[313] = {267, 90,4, 0}, -- M60 machine gun --FIXME: rot, speed, amount (if even used)

}

armour_tbl =
{
--[1] = 0, --cap
--[2] = 0, --cowboy hat
[3] = 1, --pith helmet
[4] = 2, --military helmet
--[5] = 0, --derby
--[6] = 0, --kerchief
--[7] = 0, --silk scarf
--[8] = 0, --muffler
--[9] = 0, --man's shoes
--[10] = 0, --woman's shoes
[11] = 1, --riding boots
--[12] = 0, --ruby slippers
[13] = 2, --thigh boots
[14] = 3, --hip boots
--[15] = 0, --winged shoes
--[17] = 0, --cloth jacket
--[18] = 0, --wool sweater
--[19] = 0, --cape
--[20] = 0, --duster
[21] = 1, --wool overcoat
[22] = 2, --sheepskin jacket
[23] = 2, --arctic parka
--[25] = 0, --cotton pants
--[26] = 0, --cotton dress
[27] = 1, --denim jeans
--[28] = 0, --wool pants
--[29] = 0, --wool dress
[30] = 3, --chaps and jeans
--[31] = 0, --man's shoes
--[33] = 0, --lady's silk gloves
[34] = 1, --driving gloves
--[35] = 0, --cotton work gloves
[36] = 2, --work gloves
--[37] = 0, --wool mittens
[38] = 1, --rubber gloves
[39] = 3, --welding gloves
--[90] = 0, --electric belt?
--[234] = 0, --martian jewelry
}

function actor_get_ac(actor)
   local ac = 0

   for obj in actor_inventory(actor) do
      if obj.readied then

         local armour = armour_tbl[obj.obj_n]
         if armour ~= nil then
            ac = ac + armour
         end
      end
   end
   return ac
end

local clothing_warmth_tbl = {
[1] =    1, -- OBJ_CAP
[2] =    2, -- OBJ_COWBOY_HAT
[3] =    3, -- OBJ_PITH_HELMET
[4] =    3, -- OBJ_MILITARY_HELMET
[5] =    2, -- OBJ_DERBY
[6] =    1, -- OBJ_KERCHIEF
[7] =    1, -- OBJ_SILK_SCARF
[8] =    2, -- OBJ_MUFFLER
[9] =    1, -- OBJ_MANS_SHOES
[10] =   1, -- OBJ_WOMANS_SHOES
[11] =   2, -- OBJ_RIDING_BOOTS
[14] =   4, -- OBJ_HIP_BOOTS
[13] =   3, -- OBJ_THIGH_BOOTS
[17] =   2, -- OBJ_CLOTH_JACKET
[18] =   3, -- OBJ_WOOL_SWEATER
[19] =   3, -- OBJ_CAPE
[20] =   4, -- OBJ_DUSTER
[21] =   4, -- OBJ_WOOL_OVERCOAT
[22] =   3, -- OBJ_SHEEPSKIN_JACKET
[23] =   5, -- OBJ_ARCTIC_PARKA
[90] =  12, -- OBJ_ELECTRIC_BELT
[25] =   2, -- OBJ_COTTON_PANTS
[26] =   2, -- OBJ_COTTON_DRESS
[27] =   2, -- OBJ_DENIM_JEANS
[28] =   3, -- OBJ_WOOL_PANTS
[29] =   3, -- OBJ_WOOL_DRESS
[30] =   3, -- OBJ_CHAPS_AND_JEANS
[33] =   1, -- OBJ_LADYS_SILK_GLOVES
[34] =   1, -- OBJ_DRIVING_GLOVES
[35] =   1, -- OBJ_COTTON_WORK_GLOVES
[36] =   2, -- OBJ_WORK_GLOVES
[37] =   3, -- OBJ_WOOL_MITTENS
[38] =   1, -- OBJ_RUBBER_GLOVES
[39] =   3, -- OBJ_WELDING_GLOVES
}

function clothing_get_warmth_rating(obj)
   local rating = clothing_warmth_tbl[obj.obj_n]
   if rating == nil then
      rating = 0
   end

   return rating
end

local lit_lightsource_tbl = {
[110] =  1, -- OBJ_LIT_TORCH
[112] =  1, -- OBJ_LIT_CANDLE
[114] =  1, -- OBJ_LIT_CANDELABRA
[116] =  1, -- OBJ_LIT_OIL_LAMP
[118] =  1, -- OBJ_LIT_LANTERN
}

function is_lit_lightsource(obj)
   if lit_lightsource_tbl[obj.obj_n] ~= nil then
      return true
   end

   return false
end

function actor_str_adj(actor)
   local actor_num = actor.actor_num
   local str = actor.str

   if actor.hypoxia then
      str = str - 3
   end

   if actor_is_affected_by_purple_berries(actor_num) then
      str = str - 3
   end

   if actor.frenzy then
      str = str + 3
      if str > 30 then
         str = 30
      end
   end

   if str <= 3 then
      return 1
   end

   return str
end

function actor_dex_adj(actor)

   local dex = actor.dex
   if actor.hypoxia then
      if dex <= 3 then
         dex = 1
      else
         dex = dex - 3
      end
   end

   if actor.frenzy then
      dex = dex + 3
      if dex >= 30 then
         dex = 30
      end
   end

   if actor.asleep then
      dex = 1
   end

   return dex
end

function actor_int_adj(actor)
   local int = actor.int

   if actor.hypoxia == true or (actor.frenzy and actor.actor_num ~= 1) then
      int = int - 3
   end

   if int < 1 then int = 1 end

   return int
end

function actor_map_dmg(actor, map_x, map_y, map_z)
   local obj_n = actor.obj_n
   local actor_type = actor_tbl[obj_n]
   local player_loc = player_get_location()


   if is_actor_stat_bit_set(obj_n, 8) or is_actor_stat_bit_set(obj_n, 10)
           or actor.z ~= player_loc.z
           or actor_find_max_wrapped_xy_distance(actor, player_loc.x, player_loc.y) > 40 then
      return
   end

   if actor.alive == false or actor.hit_flag == true then
      return
   end
--print("actor_map_dmg("..actor.actor_num..")\n")

   for obj in objs_at_loc(actor.x, actor.y, actor.z) do
      local tile_num = obj.tile_num_original
      if tile_get_flag(tile_num, 3, 2) == false and tile_get_flag(tile_num, 1, 3) == true then --force passable and damaging
         actor_take_damage_from_obj(actor, obj)
      end

   end

   actor_update_frame(actor, actor.direction)
end

function actor_take_damage_from_obj(actor, obj)
   if actor_immune_to_dmg(actor) or actor.alive == false then
      return
   end

   local obj_n = obj.obj_n
   local damage = 0
   local damage_mode = 0

   if obj_n == 168 then --OBJ_PORCUPOD
      damage = math.random(1, 0x14)
   elseif obj_n == 209 then --OBJ_STEAM_LEAK
      damage_mode = 3
      if actor.actor_num ~= 6 then
         damage = math.random(math.floor(actor.hp / 2), math.floor(actor.max_hp / 2))
      end
   elseif obj_n == 215 then --OBJ_POWER_CABLE1
      play_md_sfx(0x15)
      damage = math.random(1, 0x14) + math.random(1, 0x14)
   elseif obj_n == 381 then --OBJ_DUST_DEVIL
      damage = math.random(1, 0xa) + math.random(1, 0xa)
   elseif obj_n == 463 then --hidden effect obj
      --FIXME here
   end

   actor_hit(actor, damage, damage_mode)
end

function actor_remove_charm(actor)

   actor.charmed = false;
   actor.align = actor.old_align

   if actor.in_party then
      actor.align = ALIGNMENT_GOOD
   end

   if party_is_in_combat_mode() then
      actor.wt = actor.combat_mode
   else
      if player_is_in_solo_mode() then
         actor.wt = WT_NOTHING
      else
         actor.wt = WT_FOLLOW
      end
   end

   party_update_leader()

end

function advance_time(num_turns)
   --FIXME
   local rand = math.random
   local hour = clock_get_hour()

   local quake = Actor.get_talk_flag(0x46, 3) --rasputin

   if quake then
      if rand(0, 4) == 0 then
         quake_start(1, 200)
      end
   end

   local max_light = 0

   local actor_num
   for actor_num=0,0xff do
      local actor = Actor.get(actor_num)
      if actor.alive then
         if g_in_dream_mode == false and actor.in_party and actor_num ~= 0 then
            if num_turns > 0 and actor.asleep == false then --FIXME I think we also need to check distance < 0x27 from either mapwindow or player.
               if actor_num == 6 or Actor.get_talk_flag(0x10, 5) == false or Actor.inv_has_obj_n(actor, 131) then --OBJ_BLOB_OF_OXIUM
                  if actor.hypoxia then
                     actor.hypoxia = false
                     printfl("BREATHES_EASIER", actor.name)
                  end
               else
                  if actor.hypoxia == false then
                     actor.hypoxia = true
                     printfl("GASPS_FOR_AIR", actor.name)
                  end
               end

               local warmth_rating = 0
               local obj
               for obj in actor_inventory(actor) do
                  if obj.readied then
                     warmth_rating = warmth_rating + clothing_get_warmth_rating(obj)

                     if is_lit_lightsource(obj) then
                        if rand(0, 1) == 1 then
                           if obj.quality <= num_turns then
                              if obj.obj_num == 116 --OBJ_LIT_OIL_LAMP
                                 or obj.obj_num == 118 then --OBJ_LIT_LANTERN
                                 if obj.obj_num == 116 then --OBJ_LIT_OIL_LAMP
                                    obj.obj_n = 115 --OBJ_OIL_LAMP
                                 else --OBJ_LIT_LANTERN
                                    obj.obj_n = 117--OBJ_LANTERN
                                 end
                                 obj.quality = 0
                                 printfl("THE_IS_OUT_OF_FUEL", obj.name)
                              else
                                 printfl("WENT_OUT", obj.look_string)
                                 Obj.removeFromEngine(obj)
                              end
                           else
                              obj.quality = obj.quality - num_turns
                           end
                        end
                     --FIXME update max_light here. probably not needed as light is updated elsewhere in nuvie.
                     --if max_light < obj.light then
                     --max_light = obj.light
                     --end
                     end
                  end
               end

               if g_party_is_warm or actor.z ~= 0 then
                  if actor.cold then
                     actor.cold = false
                     printfl("FEELS_WARMER", actor.name)
                  end
               else
                  local cold_status = 0

                  if hour <= 3 or hour >= 22 then
                     if warmth_rating >= 10 then
                        cold_status = 1
                     else
                        cold_status = 2
                     end
                  elseif hour <= 6 or hour >= 18 then
                     if warmth_rating < 10 then
                        cold_status = 1
                     end
                  end

                  if actor_num == 6 then
                     cold_status = 0
                  end

                  if cold_status == 0 then
                     if actor.cold then
                        actor.cold = false
                        printfl("FEELS_WARMER", actor.name)
                     end
                  else
                     if actor.cold == false then
                        actor.cold = true
                        printfl("IS_FREEZING", actor.name)
                     end
                     if num_turns ~= 0 then
                        for i=1,num_turns do
                           if rand(0, 1) == 0 then
                              if cold_status == 2 then
                                 printfl("IS_FREEZING", actor.name)
                                 actor_hit(actor, rand(1, 2))
                              end
                           else
                              printfl("IS_FREEZING", actor.name)
                              actor_hit(actor, rand(1, 2))
                              if cold_status == 2 then
                                 actor_hit(actor, rand(1, 2))
                              end
                           end
                        end
                     end
                  end
               end

            end
         end

         if num_turns ~= 0 then
            for i=1,num_turns do
               --FIXME what does  word_4E6FA do?

               --remove battle frenzy from actor when the party exits combat mode
               if actor.frenzy and not party_is_in_combat_mode() then
                  actor.frenzy = false
               end

               if actor.poisoned then
                  if rand(0, 25) == 0 then
                     actor.poisoned = false
                  end
               end

               if actor.charmed and rand(0, 0x19) == 0 then
                  actor_remove_charm(actor)
               end

               if actor.paralyzed then
                  if actor_num == 6 or (rand(0, 3) == 0 and actor.str >= rand(1, 0x1e)) then --FIXME used adjusted str
                     actor.paralyzed = false
                  end
               end

               if actor.asleep and actor.wt ~= WT_SLEEP and (not g_party_is_warm or not actor.in_party) then
                  --FIXME check sub_2B0EC(actor.x,actor.y,actor.z)
                  if rand(0,0x14) == 0 then
                     actor.asleep = false
                     --FIXME bit 3 set on 1af1 flags
                  end
               end

               if actor.poisoned and actor_num ~= 6 and rand(0, 7) == 0 then
                  actor_hit(actor, 1)
               end

               if actor_num < 8 then
                  actor_decrement_berry_counters(actor)

                  for obj in actor_inventory(actor, true) do
                     local obj_n = obj.obj_n
                     if obj_n == 160 and obj.frame_n > 1 then --OBJ_EMPTY_BUCKET with ice
                        if rand(0, 100) < 10 then
                           printl("SOME_ICE_HAS_MELTED")
                           obj.frame_n = 1
                        end
                     elseif obj_n == 256 then --OBJ_CHUNK_OF_ICE
                        if rand(0, 100) < 10 then
                           printl("SOME_ICE_HAS_MELTED")
                           Obj.removeFromEngine(obj)
                        end
                     elseif obj_n == 448 or (obj_n == 449 and actor_num ~= 6 and actor.poisoned == false) then --OBJ_BLOCK_OF_RADIUM, OBJ_CHIP_OF_RADIUM
                        if obj.in_container == false or obj.parent.obj_n ~= 139 then --OBJ_LEAD_BOX
                           printfl("IS_POISONED", actor.name)
                           actor.poisoned = true
                        end
                     end
                  end
               end

            end
         end

         actor_map_dmg(actor, actor.x, actor.y, actor.z)
         actor.hit_flag = false
      end
   end

   local minute = clock_get_minute()

   clock_inc(num_turns)

   if minute + num_turns >= 60 then

      update_watch_tile()

      update_actor_schedules()
      if g_hours_till_next_healing > 0 then
         g_hours_till_next_healing = g_hours_till_next_healing - 1
      end

      update_lamp_posts()

      local blue_berry_counter = actor_get_blue_berry_counter()
      if blue_berry_counter > 0 then
         actor_get_blue_berry_counter(blue_berry_counter - 1)
      end

      if not g_party_is_warm and not g_in_dream_mode and Actor.get_talk_flag(0x10, 5) then
         for actor in party_members() do
            if actor.actor_num ~= 6 and not actor.asleep then
               local oxium = Actor.inv_get_obj_n(actor, 131) --OBJ_BLOB_OF_OXIUM
               if oxium ~= nil then
                  if oxium.qty > 1 then
                     oxium.qty = oxium.qty - 1
                     if actor.hypoxia then
                        actor.hypoxia = false
                        printfl("BREATHES_EASIER", actor.name)
                     end
                  else
                     Obj.removeFromEngine(oxium)
                     if Actor.inv_get_obj_n(actor, 131) ~= nil then
                        if actor.hypoxia then
                           actor.hypoxia = false
                           printfl("BREATHES_EASIER", actor.name)
                        end
                     else
                        if actor.hypoxia == false then
                           actor.hypoxia = true
                           printfl("GASPS_FOR_AIR", actor.name)
                        end
                     end
                  end
               else
                  if actor.hypoxia == false then
                     actor.hypoxia = true
                     printfl("GASPS_FOR_AIR", actor.name)
                  end
               end
            end
         end
      end

   end
end

function can_get_obj_override(obj)
   return false
end

function subtract_movement_pts(actor, points)
   if actor.actor_num < 16 then
      if party_is_in_combat_mode() == false then
         points = points - 2
      end
   end

   if points < 1 then
      points = 1
   end

   actor.mpts = actor.mpts - points
end

function actor_radiation_check(actor, obj)
   if obj.obj_n == 448 or obj.obj_n == 449 then --OBJ_BLOCK_OF_RADIUM, OBJ_CHIP_OF_RADIUM

      local actor_num = actor.actor_num
      if actor_num == 6
         or actor_is_affected_by_purple_berries(actor_num) -- purple berries protect against radiation
         or Actor.inv_get_readied_obj_n(actor, ARM) == 136 --OBJ_TONGS
         or Actor.inv_get_readied_obj_n(actor, ARM_2) == 136 then
         return
      end

      actor.poisoned = true
      printl("OUCH_IT_IS_VERY_HOT")
   end
end

function actor_get_obj(actor, obj, container) -- FIXME need to limit inventory slots

   --FIXME handle getting into container.

	if obj.getable == false then
		printnl("THAT_IS_NOT_POSSIBLE")
		return false
	end

   if actor_find_max_wrapped_xy_distance(actor, obj.x, obj.y) > 1 then
      play_md_sfx(0x32)
      projectile_anim(obj.tile_num, obj.x, obj.y, actor.x, actor.y, 4, false, 0)
   end

	if Actor.can_carry_obj_weight(actor, obj) == false then
		printl("THE_TOTAL_IS_TOO_HEAVY")
		return false
	end

   if not Actor.can_carry_obj(actor, obj) then
      printnl("YOU_ARE_CARRYING_TOO_MUCH_ALREADY")
      return false
   end

   subtract_movement_pts(actor, 3)

   actor_radiation_check(actor, obj)

   if obj.obj_n == 256 then -- OBJ_CHUNK_OF_ICE
      printnl("THE_ICE_IS_MELTING")
   end

   if obj.obj_n == 110 -- OBJ_LIT_TORCH
           or obj.obj_n == 112
           or obj.obj_n == 114
           or obj.obj_n == 116
           or obj.obj_n == 118 then
      if not actor_has_free_arm(actor) then
         printl("YOUR_HANDS_ARE_FULL")
         return false
      end

      Obj.moveToInv(obj, actor.actor_num)
      Actor.inv_ready_obj(actor, obj)
      if obj.obj_n == 110 then -- OBJ_LIT_TORCH
         obj.quality = 0xb4
      end

      advance_time(0)

      return true
   end

   if obj.obj_n == 411 -- OBJ_SWITCH_BAR
           or obj.obj_n == 458 -- OBJ_PANEL
           or obj.obj_n == 314 -- OBJ_TRACKING_MOTOR
           or obj.obj_n == 206 then -- OBJ_TIFFANY_LENS
      if obj.quality % 2 == 0 then
         printnl("WONT_BUDGE")
         return false
      end
   end

   Obj.moveToInv(obj, actor.actor_num)

	return true
end

function actor_get_max_hp(actor)
   if actor.actor_num == 6 then
      return 0xf0
   end

   if actor.in_party then
      return actor.str * 2 + actor.level * 24
   end

   --FIXME return actor max hp from stat table.
   return 1;
end



--function actor_take_hit(actor, damage)
--   local hp = actor.hp
--   if damage >= hp and actor.actor_num == 1 then
--      hit_anim(actor.x, actor.y)
--      actor.hp = 0
--   else
--      Actor.hit(actor, damage)
--   end
--end

function kill_actor(actor)
   actor.hp=0
   actor_dead(actor)
end

function actor_dead(actor)
   --FIXME
end

function get_portrait_number(actor)
   local NO_PORTRAIT = 255
   local idx = actor.actor_num
   if idx >= 0xa and idx <= 0xf then
      idx = 0x51
   end

   if idx >= 0xbf then
      return NO_PORTRAIT
   end

   if idx <= 1 then
      if player_get_gender() == 1 then
         idx = 0
      else
         idx = 1
      end
   elseif idx == 6 and Actor.get_talk_flag(actor, 1) then
      idx = 0x16
   elseif actor.obj_flag_0 then
      if player_get_gender() == 1 then
         idx = 0x73
      else
         idx = 0x72
      end
   elseif idx == 0x21 and not Actor.get_talk_flag(actor, 2) then
      idx = 0x61
   elseif idx == 0x15 or (idx >= 0x47 and idx <= 0x4a) or idx == 0x4e then
      idx = 0x14
   elseif idx >= 0x4b and idx <= 0x4d then
      idx = 0x16
   elseif idx == 0x1e then
      idx = 0x18
   elseif idx == 0x46 then
      idx = 0x40
   elseif idx >= 0x50 and idx <= 0x57 then
      idx = idx - 0x26
   elseif idx == 0x5e then
      idx = 0x6
   elseif idx == 0x68 or idx == 0x77 or idx == 0x78 then
      idx = 0x67
   elseif idx == 0x69 then
      idx = 0x19
   elseif idx == 0x6d then
      idx = 0x2
   end

   return idx
end

function find_rockworm_actor(obj)
   if obj ~= nil then
      if obj.obj_n == 371 or obj.obj_n == 370 then --OBJ_ROCKWORM1, OBJ_ROCKWORM_BASE
         local actor = Actor.get(obj.quality)
         if actor.alive then
            return actor
         end
      end
   end

   return obj
end