summaryrefslogblamecommitdiff
path: root/src/map/script-fun.cpp
blob: ce804175b9713e63fac3e294b620f7ed6a6d182f (plain) (tree)
1
2
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2678
2679
2680
2681
2682
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736
2737
2738
2739
2740
2741
2742
2743
2744
2745
2746
2747
2748
2749
2750
2751
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801
2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885
2886
2887
2888
2889
2890
2891
2892
2893
2894
2895
2896
2897
2898
2899
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913
2914
2915
2916
2917
2918
2919
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934
2935
2936
2937
2938
2939
2940
2941
2942
2943
2944
2945
2946
2947
2948
2949
2950
2951
2952
2953
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
2989
2990
2991
2992
2993
2994
2995
2996
2997
2998
2999
3000
3001
3002
3003
3004
3005
3006
3007
3008
3009
3010
3011
3012
3013
3014
3015
3016
3017
3018
3019
3020
3021
3022
3023
3024
3025
3026
3027
3028
3029
3030
3031
3032
3033
3034
3035
3036
3037
3038
3039
3040
3041
3042
3043

                                                                    




















                                                                           
 
                            
                               
 
                            
                                  


                                     
                             
                            
 

                           

                                          


                                  

                        
                          

                    
                      

                     


                  
                        

                    


                                    

                      
                           
                   
 

                        


              

             
 

                                                             
 

                                                                                                                    















                                                                                                 

     






                          
  
                                          
  
                                            
  

                                            

                                 
 
                                                      
                                              







                                                          
                                                      
                                              
                                             


                                            
  

                                            

                                  
 
                                     
     
                                                         
                                        
                
     


                                             


                                            
                                          
                                 

                                            

                                      
 
                                         
                                                              
 
                       
     



                                              
     

                                                              
      



                                                                                                                                                               
 





                                                
                                                                                   
                                            
                    
         
     
                  

 



                                            


















                                                                  



                                                                                                                                                                   






















                                                                                       



                                                                                                                                                           







                                         



                                            















                                                                                         






                                                              
               
     


                                                    



                                            





                                
                                            
                                       
                        

                                            

                                     
 






                                                      
 



                                                                                                                                                       



                                     


                                            
                                                       
                                                 

                                            
      
                                    
 
                                                                                          






                                                                                              
 
                
                                                                           
                                            
     
 
                                        


                                            
  

                                            
      
                                  
 
                                                      
                                              
                                     
                                 

 
                                            
  

                                            
      
                                   
 
                                                                                        






                                                                                       
                                    
                                                      
                                              
 



                                                 
 
 



                                            



                                     
                                                      
                                              



                                                 


                                            
  

                                            

                                  
 
                                                      
                                              
 
                                     
     


                                              
 

                                                                 
         




                                                                
         
                                                                  


        

                                           
         



                                            
         
 







                                                                                 
         

                                                       
             
                                                                
                                                
                        
             

                                                                            
         
     


                                            
  

                                            



                                  
     




                                                                  

        
     



                                                                                
 



                                            
      




















                                                            
                                            





















                                                               



                                            


                                 
                              
 
                
     














                                                            

                                                        
















                                                               




                                            



                                            
      










                                                    



                                            
      




                                                                                       



                                            





                                                                                       



                                            





                                                                                                              








                                                      
 


                                                                      
 
                                              



                                              


                                            
  

                                            

                                  
 

                                                      


                                                                      
                                              
 
                                                   


                                            
                           
                        

                                            

                                                                                 
 
                                                    
                                                       
 
 




                                      
 







                                                                          
 
                                                                    
 




                                                                         


                                            
  

                                            
      
                                  
 
               
                                                      


                                
                                              
 





                                                                            
 

                                                         
        
                            

 
                                            
  

                                            
      











                                                                                   
                                             

















                                                                                       
                                    
 






                                                                                                     





                                                
                    
     





                                                                                                 

     









                                                                                                 
 






                                                                                                        
                                                         







                                                                                                             




                                                           













                                                                                   
 


                                                       
                                                                              
                               


                                                              
                                                    








                                            
                                   
 


                                            
 



                                                      
 
                           
                                              
                                
     


                                    
         
                                                                  
         
            
         



                                                                         
             

                                                                                                
             
 

                                                                     
     
        
     






                                                        
     

 



                                            

















                                                            
                


                           
                                              













































                                                                                                         
                                            








                                                                                  
                                            




















                                                                            



                                            







                                                      

                               
                                              
 


                                                                            
                





                                    
                                    
















                                                                            
                                            
  

                                            
      
                                 
 


                                 



                              
                                                 
                                        
                                                                          
                                          
                                                                 






                                             



                                            
      





                                                          



                                            
      













                                                       
                                                 
                                        
                                                                          
                                          
                                                                 






                                             



                                            














                                                       


               
                                                 
                                        
                                                                          
                                          
                                                                 
                                             
     
                                
     
                 

 
                                            












                                                                                 




                                            




                                     
                            























                                                                          


                                 


                   




                                                                     
                                              
 
                                                                               




                        
 




                                          
                                     

               

                                  




                                                   

                                                    
               
 



                                                                            
                                                  


                              
                         
                 

                        






                                          




                                    






                                                                      













                                                                                  

                            
                   
                               




                               




                                                                                          















                                                     

                                 





















































                                                                                                     

                                            
               
                    

                                            
      
                                 
 

                                      
                                                    
     
                         












                                                                         












                                                                                            




                                   
         
                                   
                                                      
         
 
                                         
                                                   





                                                        


                                             

                      






                                                 
                                             



































                                                                               
                                             
                 
                                                         

                           
                                            

                
                                               



                          


                       
                             
                                             
                                                      


        
                                 
                                         
                                                      
     
 

 



                                                                                             





































                                                                 
                                            
                     
                          

                                            
      
                                      
 
                                      



                                                        
                     
 
                                                        
     
                                                         

               








                                                         
                                            









                                                       
                                            
                
                                                      
         
                                               



                                                       
     
                                       
                                                  
     
 
                                                            
     
                                    
                                                      

                                                                                      
            
                                                                                      
     


                                            
                        
                           

                                            
      
                                        
 
                                      

                                                        

                               
                                    
 
                                                        
     
                                                           
               
     
                                                  
                                    
                                                       
     
                                       
                                                  
     

                                
     
                                    
                                                      

                                                                                      
            
                                                                                      
     
 

 

                                            
                                    

                                            
      
                                          
 



                                                           
                        
         
                                                


                             
                         
             
                                                


                           
                         

             
                     
                

                 

 



                                            
      
                                          
 


                                                        
 
                                                        
     
                                                          
               
     

                                                              


                                            
                                                  
                                                          

                                            

                                               
 
                                                       
     

                                       
         
                                                                                             

                                                  
         
            
         

                                                   
         
     

        
                                                                                  

                                              

 



                                            
      














































                                                                    



                                            
      








                                                      



                                            




                                                      
                                              


                                                             



                                            
      
                                     
 
                                                      

                                             
                                              
 
                                 
 

 



                                            
      
                                       
 


                                  
 
                             
 
                           
                                              
 




                                                   
                                                                         

                                                 
                                       

                      


                                                      
 










                                                         
                                                                  

                                              
 

 
                                            
                     
               

                                            
      
                                         
 





                                  
                                              



                                  
     
                                                   
                                                                         

                                                 
                                       

                      






                                                      
                                                                                                                 
                                              
               
     
 








                                                                     


                                            
  

                                            
      
                                     
 



                                  
 
                           
                                              
 




                                                   
                                                                         

                                                 
                                       

                      

        



                                                      
     






                                                                               
                                                                                                   
                                                                  
                                                                                                                                                      








                                                                           
     
 
 
 
                                            
  

                                            
      
                                      
 




                                  
 
                           
 




                                                   
                                                                         

                                                 
                                       

                      


                                                      
 




                                                                          


                                                              






                                                                                
     





                                            
      
                                     
 





                                  
                                              





                                                   
                                                                         

                                                 
                                       

                      







                                                      
                                                                              






















                                                            

 


                                        
                                                      
                                              
                                                                                  

 
                                            

                                          

                                            
      
                                       
 

                                  
 


                                                                               
        
                               
 



                                               
 







                                                                                         


                                            










                                                                              
                                    









                                                                   

                                    

                                            
      
                                                  
 
                                                 
 
                                                             


                                            
                           
                   

                                            
      
                                         
 

                                  
 
                                                                                                           
                                                                     


                               
                                              
 


















                                                                    
 
 
 

















                                         
                                            
                                 

                                            
      
                                        
 

                                  
 



                                                                               
 
                                              



                                                
                                                                  











                                                                                     

        


                                               
 
 
                                            


















                                            
                                          
                                

                                            
      
                                   
 




                                                    

                                                      
                                              
                            
 
 
 
                                            
                                          
                                

                                            
      
                                    
 




                                                    


                                                      
                                              

                                    


                                            
                  
               

                                            
      
                                   
 

                                  
 



                                                 
                           
                                              

                                  
 

 
                                            
                                          

                                            
      
                                      
 

                                  
 


                                                              
                                              
 



                                             

 
                                            
                           
                     

                                            
      
                                        
 
                                                      
                                                 
                                              
                                                              
 
 
                                            
  

                                            
      


                                                      
                                              
 






                                                                                          
 

                                
 
                                            
                                                                        











                                                                    






                                            
                                        
 
                                                      
                                              

                                                                   
 
 






                                            
                                                                                        






                                                                                       
                                    
 
 
                                            
                                     

                                            
      
                                     
 
                                  
 
                           
                                              
 
                                                                        
 
 
 
                                            
                       

                                            
      
                                     
 
                                  
 

                                                 
                                              
 
                             
               


                          
 
 
 
                                            

                                 

                                            
      
                                       
 
                                                      
             
                                                                      
                                              
 

                               
 
                                                  


                                            
                    
  




                                           

                                            
      
                                                                
 

                                  
 
                 
     

                                                                          
         



                                                                 
         








                                                                                     
     


                                            



                                          

                                            
      
                                                            
 
                                      
 
                               
 
                 
     























                                                                 
     


                                            
                           
                              

                                            
      
                                         
 


                                                                                 
                                              
 



                                           
 
                            


                                            
                           
                                

                                            
      
                                    
 

                                                      
                                              
 





                                                                    
 
 
 
                                            
































































































































































































































































































































































































































                                                                                                                                               


                                            








                                                                                    



                                                                                            
                
                                                         
 





                                                  



                                                                                                   


                                                                                 








                                                   
                                              



































                                                                                                        
                                            
                        
                   

                                            
      
                                     
 


                      
 







                                                                          
 


                                                                             
 

                                            
                   







                                            
 









                                                                          
 

                                                                                    


                                            
                        
                  

                                            
      
                                                                     
 

                                         
     








                                                               
               
     
 
 



                                            







                                                                          
 
                                                                    




                                                                        
 
 
                                            
                              
                             

                                            
      
                                        
 



                                                     
 
 
                                            
                                 
                  

                                            
      
                                      
 
                                  



                                                                      





                                                                     
                                              

                                      

 
                                            
                                    
                      








                                                                      
                                                           


                                            
                           
                           

                                            
      
                                          
 


                                                                               
        
                                     
                                     




                                                           


                                            
                        
                  

                                            
      
                                           
 


                                                                               
        
                                     
                                     

                                                           
 
                             


                                            
                        
                 

                                            
      
                                          
 




                                                                               
                                     

                                                           
 
                            


                                            
                              
                               

                                            
      
                                         
 






                                                                               
                                     

                                                           
 












                                                     


                                            
                           
                           

                                            
      
                                         
 





                                                                               
                                     

                                                           
 
                                     
 
 



                                            
      






                                                      
                                              















                                                                                   



                                            
      


                                                      
                                              
 







                                                                                                                 
                             




























                                                                                                     



                                            
      











                                                                               
                                     
 













                                                          
                                                  









                                                   
                                            
                           
                               

                                            
      
                                      
 


                                                  
 






                                    
                                                  



                                      
 

                                            
                                                
                                              

                                            
      
                                                                            
 

                                      
 



                                            



                                         
 



                                                                          
                                                                    




                                                                                   

 
                                            
                        
             

                                            
      
                                      
 




                                                                           
               


                                                                                                  
                  




                                            
 
 
                                            
                                       
                             

                                            







                                                                   



                                            
      
                                         
 
                  
                                                                      
                                                       


                                               
       





                                                                          

 



                                            
      

                                       

                                                                                 
     


                                                                      
 
                            

                                  

 
                                            
                                       
                              

                                            
 
      
                                                                         
 




                                                          
 



                                            








                                                                      
 




                                        
                                                       


                                               
       





                                                                                                                    


                                            
                                                   
                                          

                                            
      
                                                                                       
 
                                                  
 

                                            
 

 



                                            
      
                                                                                                 
 
                                                  
 





                                                  

 



                                            
      
                                             
 


                                                 
 








                                                                      
     
                                                   
                                                                         

                                                 
                                     

                      






                                                    
                                                       


                                               
       













                                                                                                  


                                            
                  
                

                                            
      
                                       
 

                                                                      


                                            
                  
                 

                                            
      
                                        
 

                                                                      

 
                                            
                           
                                       

                                            
      
                                      
 




                                                                          










                                                                                                   

                                                











                                            
                                  
                                                                                                           



                                                              


                                            
                        
                              

                                            
      
                                    
 

                                                             
                                                                                                           



                                                              

                                               
 



                                            




                                                             
                                                                                                           


                                                              

                                                                             
 
 
 
                                            
  

                                            
      
                                      
 
                                         
                                             
                                   
 
 
                                            
                              
               

                                            
      
                                         
 

                                  
                                              
                      
 
 
                                            
                     
             

                                            
      
                                       
 
                                                    
                                                                        


                                            
                     
             

                                            
      
                                       
 
                        
 
 
                                            
                     
                  

                                            
      
                                        
 

                                                                           
 
 



                                            
      
                                        
 


                                                                      
                                                     
                             
     
                           
     
                  
 
 



                                            
      
                                           
 
                                                                      

                                      
                                                     
                             
     
                           
     
                  
 
 



                                            
      
                                        
 
               
 


                                                                      
                                                     
                             
     
                            
     
                  
 
                                          

 



                                            
      
                                   
 
                                                                      

                                                                   

                                     
 

                                                                                           
 




                                        
                                                                                                                                  






                                                                            


                                           


                 
     

 



                                            
      
                                    
 
                                                                      
                                                                
                                  

                                     
 

                                                                                         
 




                                        
                                                                                                                                  




                                              
                                        



                 

 



                                            
      
                                           



                                                      

                 
 
                                              
                                

 



                                            


                                        
                                  
                                                                                                           
                                                                     


                               
                                              
 
                                     
                 


                 
                                        





                                                        
                                             

 
                                            

                        

                                            
      
                                     
 







                                                   
                               
               





                                                              
 
 



                                            




                                                          
 


                                                                          
                                                                    




                                                                      
 




                                                                         

 



                                            
      




                                                                          
 



                                            
      
                                                          
 




                                                                          
 
                                                           
     

                                               
       







                                                                         

 



                                            
      
                                      
 
                                                                            
                                                      
                                                           
 
                                                                      
     

                                              
     
                                          

 



                                            
      
                                     
 
                                                      
 
                                            
     

                                              
     

                                          

 



                                            
      






                                         




                                                                     
                                                              


                           
                                   





                                            



                                            
      


                                                      
                                              
                                                                              

 



                                            
      





                                                          



                                            


                                 
                                                      


                                                                



                                            
      

                                     
                                      

                                                        
                                                          




                                         
                                                        



                                                        
                                                  

                                            
     
                                             
                                                         
     
 
 




                                                                 
                                        







                                                                                      
                
                                                                                     





                                          
                                        







                                                                                      
                
                                                                                     



         
                                            
                                   
                                                  

                                            
      
                                              
 
                                                      
              
                                              
 














                                                                                         
 
 



                                            
      
                                                       
 
                                                      


                                                      
 
                                              
 
                                         
     













                                                                                           
     
                                                                                 
 

 



                                            
      
                                                         
 
                                                      
                     
 
                                              
 
                                                  

                                                
 














                                                                                           

 



                                            
      
                                       
 
                                                      
                                                       
                                              

                                      
 

 



                                            
      
                                         
 
                                                      
                                                       
                                              

                                        
 

 
                                            
                                       
                            






                                        

                                            
      
                                        
 







                                      
     







                                                                     
     
 










                                                          
     


                                                          
     



                                  


                                            
                            

                                            
      
                                           
 








                                                 
 
 
 



                                            
      
                                            
 

                                                      
                    
 



                                            
 

 



                                            


















                                                                     








                                                                                        

                                                           
                           
                 

                

                                                       
                       
             



                               

                                                   
                   
         
                                             













                                                        
                                     

                                                        
                                                  































                                                                     
                                              




               
                           
                                                     

                                                  









                                                
                    

                                
                                         



                                                                                     
                    

                                      



                                                
                                            
                 

                                            
      
                                  
 
                                                      
 
                                              

                          
     


                                                       
     
                         
 
 
 
                                            
                         

                                            
      
                                         
 
                                                      
                                              



                                                     
     


                                                       
     


                         
 
 
                                            

                                              

                                            
      
                                     
 

                                    
 



                                                                      
 
                                                                   
 
                              
 
                              
                    


                                      
 





                                      
 

 
                                            




















                                                                      
                                                                    



























                                                                                                     
     

                                
     










                                            


                                            

                                     
 

                                                                           
 



                                                           
 

 



                                            




                                                      
                                              


                                                



                                            
      


                                                      
 



                                                                               



                                                                         
                                              





                                       



                                            
      


                                                      
                                              
 











                                                                               



                                            
      







                                                                           
                                              






















                                                                                   



                                            
      



                                                      
                                              


                               



                                            
















                                          
                                              


                                 



                                            





















                                                 




                                            


                                     

                                         
 


                                                                          







                                                                               
     


                              

 
                                            


                                            


                                         



                                            
                                       


                                            


                                            
      
                                     
 
                                                      

                                             
                                              
 
                 
     


























                                                             
 

                                            
 
                                            


                                                         



                                          
                                                      
                                              
 
                                  
 


                                
     
               
         

                                                         

                  




                                                  
                  
     

 
                                            
            

                                            

                                                                                    
 

               




                                                                    

 



                                            

                                       
 










                                                                          


                            
                                                                    
 




                                



                   



                                                                            
                        
 

                                            
                                                     

                                            

                                  
 

                                                      
 




                                                                      
 
                                              
 



                                                   

 

















                                                                          



                                                    
      
                                  
 

                                                      
 
                                              
 

                                                                       
                                                                    


                                   
 
 
                                            
                               

                                            
      
                                    
 
                                                      
                                              

                                                      
 
 



                                          
      
                                         
 



                                                                                     
                                                                    






                              
 
 



                              
      
                                  
 
                                                      
                                              
                                                 
 
 



                              
      
                                  
 
                                                      
                                              
                                                 

 







                                                      
                                              


                                                                      
                                            
                                  
                                            


                                    
 

                                  
                                                                                                           


                                                                     
 

                                                                                      
               
     
 
                                                        
 
 
                                            
                     
                                            



                                        

                                     


                          




                                             

                                                                             



                                                            
                                   
         


                                    
 
                                           
 


















                                                                                        






                                     




                                                                           
                                                                        


                                    
 
                                    
 









                                                 




                                                                           
                                                                        


                                    
 
                                    
 


                                                 



                                            

                                   
 
                
 
 
 

                                        


                                     

                               

                                   
                                
                                  
                                 

                                 







                                          
                                 
                                   
                                

                                       
                               

                                 
                                
                              


                                       
                                            
                                       





                                       
                                    
                                  
                                   
                                      
                                     




                                    
                                              








                                      



                                              


                                              
                                     
                                     
                                       
                                       
                                        


                                       
                                             
                                       
                                   








                                               

                                   
                                   
                              
                                
                                     







                                         
                                        
                                     
                                   
                                     


                                   
                                     
                                      
                                    









                                                     
                                    
                                           
                                   
                                    
                                       
                                
                                 
                                     
                                              
                                

                                   
                                 
                                      
                                           
                                         
                                  
                                       

                               
                                     
                                        
                                      
                                  

                             
                               


                                     
                                
                                 
                                   
                                        
                               
                               
                                   


                              
                                  
                                    
                             
                             
                                
  
                  
                   
#include "script-fun.hpp"
//    script-fun.cpp - EAthena script frontend, engine, and library.
//
//    Copyright © ????-2004 Athena Dev Teams
//    Copyright © 2004-2011 The Mana World Development Team
//    Copyright © 2011 Chuck Miller
//    Copyright © 2011-2014 Ben Longbons <b.r.longbons@gmail.com>
//    Copyright © 2013 wushin
//
//    This file is part of The Mana World (Athena server)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include "../compat/fun.hpp"
#include "../compat/nullpo.hpp"

#include "../generic/db.hpp"
#include "../generic/dumb_ptr.hpp"
#include "../generic/intern-pool.hpp"
#include "../generic/random.hpp"

#include "../io/cxxstdio.hpp"
#include "../io/extract.hpp"

#include "../net/timer.hpp"

#include "../proto2/net-HumanTimeDiff.hpp"

#include "../high/core.hpp"
#include "../high/extract_mmo.hpp"

#include "atcommand.hpp"
#include "battle.hpp"
#include "battle_conf.hpp"
#include "chrif.hpp"
#include "clif.hpp"
#include "globals.hpp"
#include "intif.hpp"
#include "itemdb.hpp"
#include "map.hpp"
#include "mob.hpp"
#include "npc.hpp"
#include "npc-parse.hpp"
#include "party.hpp"
#include "pc.hpp"
#include "script-call-internal.hpp"
#include "script-parse-internal.hpp"
#include "script-persist.hpp"
#include "skill.hpp"
#include "storage.hpp"
#include "npc-internal.hpp"
#include "path.hpp"

#include "../poison.hpp"


namespace tmwa
{
namespace map
{

#define AARG(n) (st->stack->stack_datav[st->start + 2 + (n)])
#define HARG(n) (st->end > st->start + 2 + (n))

#define BUILTIN_NAME() (builtin_functions[st->stack->stack_datav[st->start].get_if<ScriptDataFuncRef>()->numi].name)

#define script_nullpo_end(t, error)                                                             \
    if (nullpo_chk(__FILE__, __LINE__, __PRETTY_FUNCTION__, t)) {                               \
        if (st->oid) {                                                                          \
            dumb_ptr<npc_data> nullpo_nd = map_id_is_npc(st->oid);                              \
            if (nullpo_nd && nullpo_nd->name) {                                                 \
                PRINTF("script:%s: " #error " @ %s\n"_fmt, BUILTIN_NAME(), nullpo_nd->name);    \
            } else if (nullpo_nd) {                                                             \
                PRINTF("script:%s: " #error " (unnamed npc)\n"_fmt, BUILTIN_NAME());            \
            } else {                                                                            \
                PRINTF("script:%s: " #error " (no npc)\n"_fmt, BUILTIN_NAME());                 \
            }                                                                                   \
        } else {                                                                                \
            PRINTF("script:%s: " #error " (no npc)\n"_fmt, BUILTIN_NAME());                     \
        }                                                                                       \
        st->state = ScriptEndState::END;                                                        \
        return;                                                                                 \
    }

enum class MonsterAttitude
{
    HOSTILE     = 0,
    FRIENDLY    = 1,
    SERVANT     = 2,
    FROZEN      = 3,
};
//
// 埋め込み関数 | Embedded functions
//
/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_mes(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    sd->state.npc_dialog_mes = 1;
    RString mes = HARG(0) ? conv_str(st, &AARG(0)) : ""_s;
    clif_scriptmes(sd, st->oid, mes);
}

static
void builtin_clear(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    clif_npc_action(sd, st->oid, 9, 0, 0, 0);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_goto(ScriptState *st)
{
    if (!AARG(0).is<ScriptDataPos>())
    {
        PRINTF("fatal: script: goto: not label !\n"_fmt);
        st->state = ScriptEndState::END;
        abort();
    }

    st->scriptp.pos = conv_num(st, &AARG(0));
    st->state = ScriptEndState::GOTO;
}

/*==========================================
 * ユーザー定義関数の呼び出し
 * Calling user-defined functions
 *------------------------------------------
 */
static
void builtin_callfunc(ScriptState *st)
{
    RString str = conv_str(st, &AARG(0));
    Option<P<const ScriptBuffer>> scr_ = userfunc_db.get(str);

    OMATCH_BEGIN (scr_)
    {
        OMATCH_CASE_SOME (scr)
        {
            int j = 0;
            assert (st->start + 3 == st->end);
#if 0
            for (int i = st->start + 3; i < st->end; i++, j++)
                push_copy(st->stack, i);
#endif
            push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ | Push the number of arguments
            push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ | Push current reference stack pointer
            push_int<ScriptDataInt>(st->stack, st->scriptp.pos);   // 現在のスクリプト位置をプッシュ | Push Current Script Position
            push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort()));  // 現在のスクリプトをプッシュ | Push Current Script

            st->scriptp = ScriptPointer(scr, 0);
            st->defsp = st->start + 4 + j;
            st->state = ScriptEndState::GOTO;
        }
        OMATCH_CASE_NONE ()
        {
            PRINTF("fatal: script: callfunc: function not found! [%s]\n"_fmt, str);
            st->state = ScriptEndState::END;
            abort();
        }
    }
    OMATCH_END ();
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_call(ScriptState *st)
{
    struct script_data *sdata = &AARG(0);
    get_val(st, sdata);
    RString str;
    if (sdata->is<ScriptDataStr>())
    {
        str = conv_str(st, sdata);
        Option<P<const ScriptBuffer>> scr_ = userfunc_db.get(str);
        OMATCH_BEGIN (scr_)
        {
            OMATCH_CASE_SOME (scr)
            {
                int j = 0;

                for (int i = st->start + 3; i < st->end; i++, j++)
                    push_copy(st->stack, i);

                push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ | Push the number of arguments
                push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ | Push current reference stack pointer
                push_int<ScriptDataInt>(st->stack, st->scriptp.pos);   // 現在のスクリプト位置をプッシュ | Push Current Script Position
                push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort()));  // 現在のスクリプトをプッシュ | Push Current Script

                st->scriptp = ScriptPointer(scr, 0);
                st->defsp = st->start + 4 + j;
                st->state = ScriptEndState::GOTO;
                return;
            }
            OMATCH_CASE_NONE ()
            {
                PRINTF("fatal: script: callfunc: function not found! [%s]\n"_fmt, str);
                st->state = ScriptEndState::END;
                abort();
            }
        }
        OMATCH_END ();
    }
    else
    {
        int pos_ = conv_num(st, &AARG(0));
        int j = 0;

        for (int i = st->start + 3; i < st->end; i++, j++)
            push_copy(st->stack, i);

        push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ | Push the number of arguments
        push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ | Push current reference stack pointer
        push_int<ScriptDataInt>(st->stack, st->scriptp.pos);   // 現在のスクリプト位置をプッシュ | Push Current Script Position
        push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort()));  // 現在のスクリプトをプッシュ | Push Current Script

        st->scriptp.pos = pos_;
        st->defsp = st->start + 4 + j;
        st->state = ScriptEndState::GOTO;
    }

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getarg(ScriptState *st)
{
    int arg = conv_num(st, &AARG(0));
    if(st->defsp < 1 || !(st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>()))
    {
        dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
        if(nd)
            PRINTF("builtin_getarg: no callfunc or callsub! @ %s\n"_fmt, nd->name);
        else
            PRINTF("builtin_getarg: no callfunc or callsub! (no npc)\n"_fmt);
        st->state = ScriptEndState::END;
        return;
    }

    int i = conv_num(st, &st->stack->stack_datav[st->defsp - 4]); // Number of arguments.
    if (arg > i || arg < 0 || i == 0)
    {
        const char a = 3; // ETX
        if (HARG(1))
            push_copy(st->stack, st->start + 3);
        else
            push_str<ScriptDataStr>(st->stack, VString<1>(a));
        return;
    }
    push_copy(st->stack, (st->defsp - 4 - i) + arg);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_void(ScriptState *)
{
    return;
}

/*==========================================
 * サブルーティンの呼び出し
 * Call of the Surbrutin
 *------------------------------------------
 */
static
void builtin_callsub(ScriptState *st)
{
    int pos_ = conv_num(st, &AARG(0));
    int j = 0;
    assert (st->start + 3 == st->end);
#if 0
    for (int i = st->start + 3; i < st->end; i++, j++)
        push_copy(st->stack, i);
#endif

    push_int<ScriptDataInt>(st->stack, j); // 引数の数をプッシュ | Push the number of arguments
    push_int<ScriptDataInt>(st->stack, st->defsp); // 現在の基準スタックポインタをプッシュ | Push current reference stack pointer
    push_int<ScriptDataInt>(st->stack, st->scriptp.pos);   // 現在のスクリプト位置をプッシュ | Push Current Script Position
    push_script<ScriptDataRetInfo>(st->stack, TRY_UNWRAP(st->scriptp.code, abort()));  // 現在のスクリプトをプッシュ | Push Current Script

    st->scriptp.pos = pos_;
    st->defsp = st->start + 4 + j;
    st->state = ScriptEndState::GOTO;
}

/*==========================================
 * サブルーチン/ユーザー定義関数の終了
 * Terminating Subroutines/User-Defined Functions
 *------------------------------------------
 */
static
void builtin_return(ScriptState *st)
{
    if (st->defsp < 1 || !(st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>()))
    {
        dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
        if(nd)
            PRINTF("Deprecated: return outside of callfunc or callsub! @ %s\n"_fmt, nd->name);
        else
            PRINTF("Deprecated: return outside of callfunc or callsub! (no npc)\n"_fmt);
    }

    if (HARG(0))
    {                           // 戻り値有り | Return value available
        push_copy(st->stack, st->start + 2);
    }

    st->state = ScriptEndState::RETFUNC;
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_next(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    st->state = ScriptEndState::STOP;
    clif_scriptnext(sd, st->oid);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_close(ScriptState *st)
{
    if (st->defsp >= 1 && st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>())
    {
        dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
        if(nd)
            PRINTF("Deprecated: close in a callfunc or callsub! @ %s\n"_fmt, nd->name);
        else
            PRINTF("Deprecated: close in a callfunc or callsub! (no npc)\n"_fmt);
    }
    st->state = ScriptEndState::END;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (sd->state.npc_dialog_mes)
        clif_scriptclose(sd, st->oid);
    else
        clif_npc_action(sd, st->oid, 5, 0, 0, 0);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_close2(ScriptState *st)
{
    st->state = ScriptEndState::STOP;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    if (sd->state.npc_dialog_mes)
        clif_scriptclose(sd, st->oid);
    else
        clif_npc_action(sd, st->oid, 5, 0, 0, 0);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_menu(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (sd->state.menu_or_input == 0)
    {
        // First half: show menu.
        st->state = ScriptEndState::RERUNLINE;
        sd->state.menu_or_input = 1;

        MString buf;
        for (int i = 0; i < (st->end - (st->start + 2)) / 2; i++)
        {
            RString choice_str = conv_str(st, &AARG(i * 2 + 0));
            if (!choice_str)
                break;
            buf += choice_str;
            buf += ':';
        }
        clif_scriptmenu(script_rid2sd(st), st->oid, AString(buf));
    }
    else
    {
        // Rerun: item is chosen from menu.
        if (sd->npc_menu == 0xff)
        {
            // cancel
            sd->state.menu_or_input = 0;
            st->state = ScriptEndState::END;
            return;
        }

        // Actually jump to the label.
        // Logic change: menu_choices is the *total* number of labels,
        // not just the displayed number that ends with the "".
        // (Would it be better to pop the stack before rerunning?)
        int menu_choices = (st->end - (st->start + 2)) / 2;
        pc_setreg(sd, SIR::from(variable_names.intern("@menu"_s)), sd->npc_menu);
        sd->state.menu_or_input = 0;
        if (sd->npc_menu > 0 && sd->npc_menu <= menu_choices)
        {
            int arg_index = (sd->npc_menu - 1) * 2 + 1;
            if (!AARG(arg_index).is<ScriptDataPos>())
            {
                PRINTF("fatal: script:menu: not a label\n"_fmt);
                st->state = ScriptEndState::END;
                abort();
            }
            st->scriptp.pos = AARG(arg_index).get_if<ScriptDataPos>()->numi;
            st->state = ScriptEndState::GOTO;
        }
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_rand(ScriptState *st)
{
    if (HARG(1))
    {
        int min = conv_num(st, &AARG(0));
        int max = conv_num(st, &AARG(1));
        if (min > max)
            std::swap(max, min);
        push_int<ScriptDataInt>(st->stack, random_::in(min, max));
    }
    else
    {
        int range = conv_num(st, &AARG(0));
        push_int<ScriptDataInt>(st->stack, range <= 0 ? 0 : random_::to(range));
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_max(ScriptState *st)
{
    int max=0, num;
    if (HARG(1))
    {
        max = conv_num(st, &AARG(0));
        for (int i = 1; HARG(i); i++)
        {
            num = conv_num(st, &AARG(i));
            if (num > max)
                max = num;
        }
    }
    else
    {
        SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
        ZString name = variable_names.outtern(reg.base());
        char prefix = name.front();
        if (prefix != '$' && prefix != '@' && prefix != '.')
        {
            PRINTF("builtin_max: illegal scope!\n"_fmt);
            st->state = ScriptEndState::END;
            return;
        }
        for (int i = reg.index(); i < 256; i++)
        {
            struct script_data vd = get_val2(st, reg.iplus(i));
            MATCH_BEGIN (vd)
            {
                MATCH_CASE (const ScriptDataInt&, u)
                {
                    if (u.numi > max)
                        max = u.numi;
                    continue;
                }
            }
            MATCH_END ();
            abort();
        }
    }

    push_int<ScriptDataInt>(st->stack, max);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_min(ScriptState *st)
{
    int min = 0xFFFFFFF6, num;

    if (HARG(1))
    {
        min = conv_num(st, &AARG(0));
        for (int i = 1; HARG(i); i++)
        {
            num = conv_num(st, &AARG(i));
            if (num < min)
                min = num;
        }
    }
    else
    {
        SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
        ZString name = variable_names.outtern(reg.base());
        char prefix = name.front();
        if (prefix != '$' && prefix != '@' && prefix != '.')
        {
            PRINTF("builtin_min: illegal scope!\n"_fmt);
            st->state = ScriptEndState::END;
            return;
        }
        for (int i = reg.index(); i < 256; i++)
        {
            struct script_data vd = get_val2(st, reg.iplus(i));
            MATCH_BEGIN (vd)
            {
                MATCH_CASE (const ScriptDataInt&, u)
                {
                    if (u.numi < min)
                        min = u.numi;
                    continue;
                }
            }
            MATCH_END ();
            abort();
        }
    }

    push_int<ScriptDataInt>(st->stack, min);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_average(ScriptState *st)
{
    int total, i;
    total = conv_num(st, &AARG(0));

    for (i = 1; HARG(i); i++)
        total += conv_num(st, &AARG(i));

    push_int<ScriptDataInt>(st->stack, (total / i));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_sqrt(ScriptState *st)
{
    push_int<ScriptDataInt>(st->stack, static_cast<int>(sqrt(conv_num(st, &AARG(0)))));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_cbrt(ScriptState *st)
{
    push_int<ScriptDataInt>(st->stack, static_cast<int>(cbrt(conv_num(st, &AARG(0)))));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_pow(ScriptState *st)
{
    push_int<ScriptDataInt>(st->stack, static_cast<int>(pow(conv_num(st, &AARG(0)), conv_num(st, &AARG(1)))));
}

/*==========================================
 * Check whether the PC is at the specified location
 *------------------------------------------
 */
static
void builtin_isat(ScriptState *st)
{
    int x, y;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));

    script_nullpo_end(sd, "player not found");

    push_int<ScriptDataInt>(st->stack,
            (x == sd->bl_x) && (y == sd->bl_y)
            && (str == sd->bl_m->name_));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_warp(ScriptState *st)
{
    int x, y;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));
    script_nullpo_end(sd, "player not found");

    pc_setpos(sd, str, x, y, BeingRemoveWhy::GONE);
}

/*==========================================
 * エリア指定ワープ
 * Area Designation Warp
 *------------------------------------------
 */
static
void builtin_areawarp_sub(dumb_ptr<block_list> bl, MapName mapname, int x, int y)
{
    dumb_ptr<map_session_data> sd = bl->is_player();
    pc_setpos(sd, mapname, x, y, BeingRemoveWhy::GONE);
}

static
void builtin_areawarp(ScriptState *st)
{
    int x, y;
    int x0, y0, x1, y1;

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x0 = conv_num(st, &AARG(1));
    y0 = conv_num(st, &AARG(2));
    x1 = conv_num(st, &AARG(3));
    y1 = conv_num(st, &AARG(4));
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(5))));
    x = conv_num(st, &AARG(6));
    y = conv_num(st, &AARG(7));

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);

    map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
            m,
            x0, y0,
            x1, y1,
            BL::PC);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_heal(ScriptState *st)
{
    int hp, sp;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    hp = conv_num(st, &AARG(0));
    sp = conv_num(st, &AARG(1));
    script_nullpo_end(sd, "player not found");

    if(sd != nullptr && (sd->status.hp < 1 && hp > 0)){
        pc_setstand(sd);
        if (battle_config.player_invincible_time > interval_t::zero())
            pc_setinvincibletimer(sd, battle_config.player_invincible_time);
        clif_resurrection(sd, 1);
    }

    if(HARG(2) && bool(conv_num(st, &AARG(2))) && hp > 0)
        pc_itemheal(sd, hp, sp);
    else
        pc_heal(sd, hp, sp);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_distance(ScriptState *st)
{
    dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
    dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
    int distance = 0;
    int mode = HARG(2) ? conv_num(st, &AARG(2)) : 0;

    switch (mode)
    {
        // TODO implement case 1 (walk distance)
        case 0:
        default:
            if (source->bl_m != target->bl_m)
            {
                // FIXME make it work even if source and target are not in the same map
                distance = 0x7fffffff;
                break;
            }
            int dx = abs(source->bl_x - target->bl_x);
            int dy = abs(source->bl_y - target->bl_y);
            distance = sqrt((dx * dx) + (dy * dy)); // Pythagoras' theorem
    }

    push_int<ScriptDataInt>(st->stack, distance);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_target(ScriptState *st)
{
    // TODO maybe scrap all this and make it use battle_ functions? (add missing functions to battle)

    dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
    dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
    int flag = conv_num(st, &AARG(2));
    int val = 0;

    if (!source || !target)
    {
        push_int<ScriptDataInt>(st->stack, val);
        return;
    }

    if (flag & 0x01)
    {
        int x0 = source->bl_x - AREA_SIZE;
        int y0 = source->bl_y - AREA_SIZE;
        int x1 = source->bl_x + AREA_SIZE;
        int y1 = source->bl_y + AREA_SIZE;
        if (target->bl_x >= x0 && target->bl_x <= x1 && target->bl_y >= y0 && target->bl_y <= y1)
            val |= 0x01; // 0x01 target is in visible range
    }

    if (flag & 0x02)
    {
        int range = battle_get_range(source);
        int x2 = source->bl_x - range;
        int y2 = source->bl_y - range;
        int x3 = source->bl_x + range;
        int y3 = source->bl_y + range;
        if (target->bl_x >= x2 && target->bl_x <= x3 && target->bl_y >= y2 && target->bl_y <= y3)
            val |= 0x02; // 0x02 target is in attack range
    }

    if (flag & 0x04)
    {
        struct walkpath_data wpd;
        if (!path_search(&wpd, source->bl_m, source->bl_x, source->bl_y, target->bl_x, target->bl_y, 0))
            val |= 0x04; // 0x04 target is walkable (has clear path to target)
    }

    // TODO 0x08 target is visible (not behind collision)

    if (flag & 0x10)
    {
        if (target->bl_type != BL::PC || (target->bl_type == BL::PC &&
            (target->bl_m->flag.get(MapFlag::PVP) || pc_iskiller(source->is_player(), target->is_player()))))
            val |= 0x10; // 0x10 target can be attacked by source (killer, killable and so on)
    }

    if (flag & 0x20)
    {
        if (battle_check_range(source, target, 0))
            val |= 0x20; // 0x20 target is in line of sight
    }

    push_int<ScriptDataInt>(st->stack, val);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_injure(ScriptState *st)
{
    dumb_ptr<block_list> source = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(0))));
    dumb_ptr<block_list> target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
    int damage_caused = conv_num(st, &AARG(2));

    if (source != nullptr && source->bl_type == BL::PC)
        pc_setstand(source->is_player());

    // display damage first, because dealing damage may deallocate the target.
    clif_damage(source, target,
            gettick(), interval_t::zero(), interval_t::zero(),
            damage_caused, 0, DamageType::NORMAL);

    battle_damage(source, target, damage_caused, 0);

    return;
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_input(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = nullptr;
    script_data& scrd = AARG(0);
    assert (scrd.is<ScriptDataVariable>());

    SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
//  char prefix = name.front();
    char postfix = name.back();

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    if (sd->state.menu_or_input)
    {
        // Second time (rerun)
        sd->state.menu_or_input = 0;
        if (postfix == '$')
        {
            set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str);
        }
        else
        {
            //commented by Lupus (check Value Number Input fix in clif.c)
            //** Fix by fritz :X keeps people from abusing old input bugs
            // wtf?
            if (sd->npc_amount < 0) //** If input amount is less then 0
            {
                clif_tradecancelled(sd);   // added "Deal has been cancelled" message by Valaris
                builtin_close(st); //** close
            }

            set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_amount);
        }
    }
    else
    {
        // First time - send prompt to client, then wait
        st->state = ScriptEndState::RERUNLINE;
        if (postfix == '$')
            clif_scriptinputstr(sd, st->oid);
        else
            clif_scriptinput(sd, st->oid);
        sd->state.menu_or_input = 1;
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_requestitem(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = nullptr;
    script_data& scrd = AARG(0);
    assert (scrd.is<ScriptDataVariable>());
    int amount = HARG(1) ? conv_num(st, &AARG(1)) : 1;
    if (amount < 1 || amount > 16)
        amount = 1;

    SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    char prefix = name.front();
    char postfix = name.back();

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_requestitem: illegal scope!\n"_fmt);
        abort();
    }

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    if (sd->state.menu_or_input)
    {
        // Second time (rerunline)
        sd->state.menu_or_input = 0;
        RString str = sd->npc_str;
        RString val;
        const char separator = ';';
        for (int j = 0; j < amount; j++)
        {
            auto find = std::find(str.begin(), str.end(), separator);
            if (find == str.end())
                val = str.xislice_h(std::find(str.begin(), str.end(), ','));
            else
            {
                val = str.xislice_h(find);
                val = val.xislice_h(std::find(val.begin(), val.end(), ','));
                str = str.xislice_t(find + 1);
            }

            // check that the item exists in the inventory
            int num = atoi(val.c_str());
            if (num < 1)
            {
                j--;
                if (find == str.end())
                    break;
                continue;
            }
            ItemNameId nameid = wrap<ItemNameId>(num);
            for (IOff0 i : IOff0::iter())
                if (sd->status.inventory[i].nameid == nameid)
                    goto pass;
        fail:
            j--;
            if (find == str.end())
                break;
            continue;

        pass:
            // push to array
            if (postfix == '$')
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(nameid));
                RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                if (item_name == ""_s)
                    goto fail;
                if (name.startswith(".@"_s))
                {
                    struct script_data vd = script_data(ScriptDataStr{item_name});
                    set_scope_reg(st, reg.iplus(j), &vd);
                }
                else
                    set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), item_name);
            }
            else
            {
                if (name.startswith(".@"_s))
                {
                    struct script_data vd = script_data(ScriptDataInt{num});
                    set_scope_reg(st, reg.iplus(j), &vd);
                }
                else
                    set_reg(sd, VariableCode::VARIABLE, reg.iplus(j), num);
            }
            if (find == str.end())
                break;
        }
    }
    else
    {
        // First time - send prompt to client, then wait
        st->state = ScriptEndState::RERUNLINE;
        clif_scriptinputstr(sd, st->oid); // send string prompt
        clif_npc_action(sd, st->oid, 10, amount, 0, 0); // send item request
        sd->state.menu_or_input = 1;
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_requestlang(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_data& scrd = AARG(0);
    assert (scrd.is<ScriptDataVariable>());
    SIR reg = scrd.get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    char postfix = name.back();

    script_nullpo_end(sd, "player not found");

    if (postfix != '$')
    {
        PRINTF("builtin_requestlang: illegal type (expects string)!\n"_fmt);
        abort();
    }

    if (sd->state.menu_or_input)
    {
        // Second time (rerunline)
        sd->state.menu_or_input = 0;
        if (name.startswith(".@"_s))
        {
            struct script_data vd = script_data(ScriptDataStr{sd->npc_str});
            set_scope_reg(st, reg, &vd);
        }
        else
            set_reg(sd, VariableCode::VARIABLE, reg, sd->npc_str);
    }
    else
    {
        // First time - send prompt to client, then wait
        st->state = ScriptEndState::RERUNLINE;
        clif_npc_action(sd, st->oid, 0, 0, 0, 0); // send lang request
        clif_scriptinputstr(sd, st->oid); // send string prompt
        sd->state.menu_or_input = 1;
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_if (ScriptState *st)
{
    int sel, i;

    sel = conv_num(st, &AARG(0));
    st->is_true = sel ? 2 : 1;
    if (!sel)
        return;

    // 関数名をコピー | Copy function name
    push_copy(st->stack, st->start + 3);
    // 間に引数マーカを入れて | Put argument markers in between
    push_int<ScriptDataArg>(st->stack, 0);
    // 残りの引数をコピー | Copy the remaining arguments
    for (i = st->start + 4; i < st->end; i++)
    {
        push_copy(st->stack, i);
    }
    run_func(st);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_if_then_else (ScriptState *st)
{
    int condition = conv_num(st, &AARG(0));
    push_copy(st->stack, st->start + (condition ? 3 : 4));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_else (ScriptState *st)
{
    int i;

    if (st->is_true < 1)
    {
        PRINTF("builtin_else: no if statement!\n"_fmt);
        abort();
    }

    if (st->is_true > 1)
        return;

    st->is_true = 0;
    // 関数名をコピー | Copy function name
    push_copy(st->stack, st->start + 2);
    // 間に引数マーカを入れて | Put argument markers in between
    push_int<ScriptDataArg>(st->stack, 0);
    // 残りの引数をコピー | Copy the remaining arguments
    for (i = st->start + 3; i < st->end; i++)
    {
        push_copy(st->stack, i);
    }
    run_func(st);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_elif (ScriptState *st)
{
    int sel, i;

    if (st->is_true < 1)
    {
        PRINTF("builtin_elif: no if statement!\n"_fmt);
        abort();
    }
    if (st->is_true > 1)
        return;

    sel = conv_num(st, &AARG(0));
    st->is_true = sel ? 2 : 1;
    if (!sel)
        return;

    // 関数名をコピー | Copy function name
    push_copy(st->stack, st->start + 3);
    // 間に引数マーカを入れて | Put argument markers in between
    push_int<ScriptDataArg>(st->stack, 0);
    // 残りの引数をコピー | Copy the remaining arguments
    for (i = st->start + 4; i < st->end; i++)
    {
        push_copy(st->stack, i);
    }
    run_func(st);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_foreach_sub(dumb_ptr<block_list> bl, NpcEvent event, BlockId caster)
{
    // call_spell_event_script
    argrec_t arg[1] =
    {
        {"@target_id"_s, static_cast<int32_t>(unwrap<BlockId>(bl->bl_id))},
    };
    npc_event_do_l(event, caster, arg);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_foreach(ScriptState *st)
{
    int x0, y0, x1, y1, bl_num;

    dumb_ptr<block_list> sd;
    bl_num = conv_num(st, &AARG(0));
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
    x0 = conv_num(st, &AARG(2));
    y0 = conv_num(st, &AARG(3));
    x1 = conv_num(st, &AARG(4));
    y1 = conv_num(st, &AARG(5));
    ZString event_ = ZString(conv_str(st, &AARG(6)));
    BL block_type;
    NpcEvent event;
    extract(event_, &event);

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);

    switch (bl_num)
    {
        case 0:
            block_type = BL::PC;
            break;
        case 1:
            block_type = BL::NPC;
            break;
        case 2:
            block_type = BL::MOB;
            break;
        case 3:
            block_type = BL::NUL;
            break;
        default:
            return;
    }
    if (HARG(7))
        sd = map_id_is_player(wrap<BlockId>(conv_num(st, &AARG(7))));
    else if (st->rid)
        sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");

    map_foreachinarea(std::bind(builtin_foreach_sub, ph::_1, event, sd->bl_id),
            m,
            x0, y0,
            x1, y1,
            block_type);
}

/*========================================
 * Destructs a temp NPC
 *----------------------------------------
 */
static
void builtin_destroy(ScriptState *st)
{
    BlockId id;
    dumb_ptr<map_session_data> sd;

    if (HARG(0))
        id = wrap<BlockId>(conv_num(st, &AARG(0)));
    else
        id = st->oid;

    dumb_ptr<npc_data> nd = map_id_is_npc(id);
    if(!nd || nd->npc_subtype != NpcSubtype::SCRIPT)
        return;

    /* If we have a player attached, make sure it is cleared. */
    /* Not safe to call destroy if others may also be paused on this NPC! */
    if (st->rid) {
        sd = script_rid2sd(st);
        script_nullpo_end(sd, "player not found");
        npc_event_dequeue(sd);
    }

    nd = nd->is_script();
    npc_free(nd);
    st->oid = BlockId();

    if (!HARG(0))
        st->state = ScriptEndState::END;
}
/*========================================
 * Creates a temp NPC
 *----------------------------------------
 */
static
void builtin_puppet(ScriptState *st)
{
    int x, y;

    NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(3))));
    if (npc_name2id(npc) != nullptr)
    {
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    dumb_ptr<block_list> bl = map_id2bl(st->oid);
    dumb_ptr<npc_data_script> parent_nd = bl->is_npc()->is_script();
    dumb_ptr<npc_data_script> nd;
    nd.new_();

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));
    Species sprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARG(4))));

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);

    nd->bl_prev = nd->bl_next = nullptr;
    nd->scr.event_needs_map = false;

    // PlayerName::SpellName
    nd->name = npc;
    nd->sex = SEX::UNSPECIFIED;

    // Dynamically set location
    nd->bl_m = m;
    nd->bl_x = x;
    nd->bl_y = y;
    if (HARG(5) && HARG(6))
    {
        nd->scr.xs = ((conv_num(st, &AARG(5)) * 2) + 1); // do the same equation as in AST
        nd->scr.ys = ((conv_num(st, &AARG(6)) * 2) + 1);
    }
    nd->bl_id = npc_get_new_npc_id();
    nd->scr.parent = parent_nd->bl_id;
    nd->dir = DIR::S;
    nd->flag = 0;
    nd->sit = DamageType::STAND;
    nd->npc_class = sprite;
    nd->speed = 200_ms;
    nd->option = Opt0::ZERO;
    nd->opt1 = Opt1::ZERO;
    nd->opt2 = Opt2::ZERO;
    nd->opt3 = Opt3::ZERO;
    nd->scr.label_listv = parent_nd->scr.label_listv;
    nd->bl_type = BL::NPC;
    nd->npc_subtype = NpcSubtype::SCRIPT;
    npc_script++;

    nd->deletion_pending = false;

    nd->n = map_addnpc(nd->bl_m, nd);

    map_addblock(nd);
    clif_spawnnpc(nd);

    register_npc_name(nd);

    for (npc_label_list& el : parent_nd->scr.label_listv)
    {
        ScriptLabel lname = el.name;
        int pos = el.pos;

        if (lname.startswith("On"_s))
        {
            struct event_data ev {};
            ev.nd = nd;
            ev.pos = pos;
            NpcEvent buf;
            buf.npc = nd->name;
            buf.label = lname;
            ev_db.insert(buf, ev);
        }
    }

    for (npc_label_list& el : parent_nd->scr.label_listv)
    {
        int t_ = 0;
        ScriptLabel lname = el.name;
        int pos = el.pos;
        if (lname.startswith("OnTimer"_s) && extract(lname.xslice_t(7), &t_) && t_ > 0)
        {
            interval_t t = static_cast<interval_t>(t_);

            npc_timerevent_list tel {};
            tel.timer = t;
            tel.pos = pos;

            auto it = std::lower_bound(nd->scr.timer_eventv.begin(), nd->scr.timer_eventv.end(), tel,
                    [](const npc_timerevent_list& l, const npc_timerevent_list& r)
                    {
                        return l.timer < r.timer;
                    }
            );
            assert (it == nd->scr.timer_eventv.end() || it->timer != tel.timer);

            nd->scr.timer_eventv.insert(it, std::move(tel));
        }
    }

    nd->scr.timer = interval_t::zero();
    nd->scr.next_event = nd->scr.timer_eventv.begin();

    push_int<ScriptDataInt>(st->stack, unwrap<BlockId>(nd->bl_id));
}

/*==========================================
 * 変数設定
 * Variable settings
 *------------------------------------------
 */
static
void builtin_set(ScriptState *st)
{
    BlockId id;
    dumb_ptr<block_list> bl = nullptr;
    if (auto *u = AARG(0).get_if<ScriptDataParam>())
    {
        SIR reg = u->reg;
        if(HARG(2))
        {
            struct script_data *sdata = &AARG(2);
            get_val(st, sdata);
            CharName name;
            if (sdata->is<ScriptDataStr>())
            {
                name = stringish<CharName>(ZString(conv_str(st, sdata)));
                if (name.to__actual())
                    bl = map_nick2sd(name);
            }
            else
            {
                int num = conv_num(st, sdata);
                if (num >= 2000000)
                    id = wrap<BlockId>(num);
                else if (num >= 150000)
                {
                    dumb_ptr<map_session_data> p_sd = nullptr;
                    if ((p_sd = map_nick2sd(map_charid2nick(wrap<CharId>(num)))) != nullptr)
                        id = p_sd->bl_id;
                    else
                        return;
                }
                else
                    return;
                bl = map_id2bl(id);
            }
        }

        else
        {
            bl = script_rid2sd(st);
            script_nullpo_end(bl, "player not found");
        }

        int val = conv_num(st, &AARG(1));
        set_reg(bl, VariableCode::PARAM, reg, val);
        return;
    }

    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;

    ZString name = variable_names.outtern(reg.base());
    VarName name_ = stringish<VarName>(name);
    char prefix = name_.front();
    char postfix = name_.back();

    if (prefix != '$')
    {
        if(HARG(2))
        {
            struct script_data *sdata = &AARG(2);
            get_val(st, sdata);
            if(prefix == '.')
            {
                if (name_.startswith(".@"_s))
                {
                    PRINTF("builtin_set: illegal scope!\n"_fmt);
                    return;
                }
                NpcName n_name;
                if (sdata->is<ScriptDataStr>())
                {
                    n_name = stringish<NpcName>(ZString(conv_str(st, sdata)));
                    bl = npc_name2id(n_name);
                }
                else
                {
                    id = wrap<BlockId>(conv_num(st, sdata));
                    bl = map_id2bl(id);
                }
            }
            else
            {
                CharName c_name;
                if (sdata->is<ScriptDataStr>())
                {
                    c_name = stringish<CharName>(ZString(conv_str(st, sdata)));
                    if (c_name.to__actual())
                        bl = map_nick2sd(c_name);
                }
                else
                {
                    id = wrap<BlockId>(conv_num(st, sdata));
                    bl = map_id2bl(id);
                }
            }
        }
        else
        {
            if(prefix == '.')
            {
                if (name_.startswith(".@"_s))
                {
                        set_scope_reg(st, reg, &AARG(1));
                    return;
                }
                bl = map_id_is_npc(st->oid);
            }
            else
                bl = map_id_is_player(st->rid);
        }
        if (bl == nullptr)
            return;
    }

    if (postfix == '$')
    {
        // 文字列 | string
        RString str = conv_str(st, &AARG(1));
        set_reg(bl, VariableCode::VARIABLE, reg, str);
    }
    else
    {
        // 数値 | numeric value
        int val = conv_num(st, &AARG(1));
        set_reg(bl, VariableCode::VARIABLE, reg, val);
    }

}

/*==========================================
 * this is a special function that returns array index for a variable stored in another being
 *------------------------------------------
 */
static
int getarraysize2(SIR reg, dumb_ptr<block_list> bl)
{
    int i = reg.index(), c = i;
    bool zero = true; // index zero is empty
    for (; i < 256; i++)
    {
        struct script_data vd = ScriptDataVariable{reg.iplus(i)};
        get_val(bl, &vd);
        MATCH_BEGIN (vd)
        {
            MATCH_CASE (const ScriptDataStr&, u)
            {
                if (u.str[0])
                {
                    if (i == 0)
                        zero = false; // index zero is not empty
                    c = i;
                }
                continue;
            }
            MATCH_CASE (const ScriptDataInt&, u)
            {
                if (u.numi)
                {
                    if (i == 0)
                        zero = false; // index zero is not empty
                    c = i;
                }
                continue;
            }
        }
        MATCH_END ();
        abort();
    }
    return (c == 0 && zero) ? c : (c + 1);
}

/*==========================================
 * 配列変数設定
 * Array variable settings
 *------------------------------------------
 */
static
void builtin_setarray(ScriptState *st)
{
    dumb_ptr<block_list> bl = nullptr;
    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    char prefix = name.front();
    char postfix = name.back();
    int i = 1, j = 0;

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_setarray: illegal scope!\n"_fmt);
        return;
    }
    if (prefix == '.' && !name.startswith(".@"_s))
    {
        struct script_data *sdata = &AARG(1);
        get_val(st, sdata);
        i++; // 2nd argument is npc, not an array element
        if (sdata->is<ScriptDataStr>())
        {
            ZString tn = conv_str(st, sdata);
            if (tn == "this"_s || tn == "oid"_s)
                bl = map_id_is_npc(st->oid);
            else
            {
                NpcName name_ = stringish<NpcName>(tn);
                bl = npc_name2id(name_);
            }
        }
        else
        {
            int tid = conv_num(st, sdata);
            if (tid == 0)
                bl = map_id_is_npc(st->oid);
            else
               bl = map_id_is_npc(wrap<BlockId>(tid));
        }
        script_nullpo_end(bl, "npc not found");
        if (st->oid && bl->bl_id != st->oid)
            j = getarraysize2(reg, bl);
    }
    else if (prefix != '$' && !name.startswith(".@"_s))
    {
        bl = map_id_is_player(st->rid);
        script_nullpo_end(bl, "player not found");
    }

    for (; i < st->end - st->start - 2 && j < 256; i++, j++)
    {
        if (name.startswith(".@"_s))
            set_scope_reg(st, reg.iplus(j), &AARG(i));
        else if (postfix == '$')
            set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), conv_str(st, &AARG(i)));
        else
            set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), conv_num(st, &AARG(i)));
    }
}

/*==========================================
 * 配列変数クリア
 * Clearing Array Variables
 *------------------------------------------
 */
static
void builtin_cleararray(ScriptState *st)
{
    dumb_ptr<block_list> bl = nullptr;
    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    char prefix = name.front();
    char postfix = name.back();
    int sz = conv_num(st, &AARG(2));

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_cleararray: illegal scope!\n"_fmt);
        return;
    }
    if (prefix == '.' && !name.startswith(".@"_s))
        bl = map_id_is_npc(st->oid);
    else if (prefix != '$' && !name.startswith(".@"_s))
    {
        bl = map_id_is_player(st->rid);
        script_nullpo_end(bl, "player not found");
    }

    for (int i = 0; i < sz; i++)
    {
        if (name.startswith(".@"_s))
            set_scope_reg(st, reg.iplus(i), &AARG(i));
        else if (postfix == '$')
            set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), conv_str(st, &AARG(1)));
        else
            set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), conv_num(st, &AARG(1)));
    }

}

/*==========================================
 * 配列変数のサイズ所得
 * Size of the array variable income
 *------------------------------------------
 */
static
int getarraysize(ScriptState *st, SIR reg)
{
    int i = reg.index(), c = i;
    for (; i < 256; i++)
    {
        struct script_data vd = get_val2(st, reg.iplus(i));
        MATCH_BEGIN (vd)
        {
            MATCH_CASE (const ScriptDataStr&, u)
            {
                if (u.str[0])
                    c = i;
                continue;
            }
            MATCH_CASE (const ScriptDataInt&, u)
            {
                if (u.numi)
                    c = i;
                continue;
            }
        }
        MATCH_END ();
        abort();
    }
    return c + 1;
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getarraysize(ScriptState *st)
{
    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    char prefix = name.front();

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_copyarray: illegal scope!\n"_fmt);
        return;
    }

    push_int<ScriptDataInt>(st->stack, getarraysize(st, reg));
}

/*==========================================
 * 指定要素を表す値(キー)を所得する
 * Obtain a value (key) representing the specified element
 *------------------------------------------
 */
static
void builtin_getelementofarray(ScriptState *st)
{
    if (auto *u = AARG(0).get_if<ScriptDataVariable>())
    {
        int i = conv_num(st, &AARG(1));
        if (i > 255 || i < 0)
        {
            PRINTF("script: getelementofarray (operator[]): param2 illegal number: %d\n"_fmt,
                    i);
            push_int<ScriptDataInt>(st->stack, 0);
        }
        else
        {
            push_reg<ScriptDataVariable>(st->stack,
                    u->reg.iplus(i));
        }
    }
    else
    {
        PRINTF("script: getelementofarray (operator[]): param1 not named!\n"_fmt);
        push_int<ScriptDataInt>(st->stack, 0);
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_array_search(ScriptState *st)
{
    ZString needle_str = ZString(conv_str(st, &AARG(0)));
    int needle_int = conv_num(st, &AARG(0));
    SIR reg = AARG(1).get_if<ScriptDataVariable>()->reg; // haystack
    ZString name = variable_names.outtern(reg.base());
    char prefix = name.front();
    int i, c;

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_array_search: illegal scope!\n"_fmt);
        return;
    }

    i = reg.index(); c = -1;
    for (; i < 256; i++)
    {
        struct script_data vd = get_val2(st, reg.iplus(i));
        MATCH_BEGIN (vd)
        {
            MATCH_CASE (const ScriptDataStr&, u)
            {
                if (u.str == needle_str)
                {
                    c = i;
                    goto Out;
                }
                continue;
            }
            MATCH_CASE (const ScriptDataInt&, u)
            {
                if (u.numi == needle_int)
                {
                    c = i;
                    goto Out;
                }
                continue;
            }
        }
        MATCH_END ();
        abort();
    }
    Out:
    push_int<ScriptDataInt>(st->stack, c);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_wgm(ScriptState *st)
{
    ZString message = ZString(conv_str(st, &AARG(0)));

    intif_wis_message_to_gm(WISP_SERVER_NAME,
            battle_config.hack_info_GM_level,
            STRPRINTF("[GM] %s"_fmt, message));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_gmlog(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    ZString message = ZString(conv_str(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    log_atcommand(sd, STRPRINTF("{SCRIPT} %s"_fmt, message));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_setlook(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    LOOK type = LOOK(conv_num(st, &AARG(0)));
    int val = conv_num(st, &AARG(1));
    script_nullpo_end(sd, "player not found");

    pc_changelook(sd, type, val);

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_countitem(ScriptState *st)
{
    ItemNameId nameid;
    int count = 0;
    dumb_ptr<map_session_data> sd;

    struct script_data *data;

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    data = &AARG(0);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            nameid = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        nameid = wrap<ItemNameId>(conv_num(st, data));

    if (nameid)
    {
        for (IOff0 i : IOff0::iter())
        {
            if (sd->status.inventory[i].nameid == nameid)
                count += sd->status.inventory[i].amount;
        }
    }
    else
    {
        if (battle_config.error_log)
            PRINTF("wrong item ID: countitem (%i)\n"_fmt, nameid);
    }
    push_int<ScriptDataInt>(st->stack, count);

}

/*==========================================
 * 重量チェック
 * Weight check
 *------------------------------------------
 */
static
void builtin_checkweight(ScriptState *st)
{
    ItemNameId nameid;
    int amount;
    dumb_ptr<map_session_data> sd;
    struct script_data *data;

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    data = &AARG(0);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            nameid = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        nameid = wrap<ItemNameId>(conv_num(st, data));

    amount = conv_num(st, &AARG(1));
    if (amount <= 0 || !nameid)
    {
        //If it gets the wrong item ID or the amount<=0, don't count its weight (assume it's a non-existent item)
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    if (itemdb_weight(nameid) * amount + sd->weight > sd->max_weight)
    {
        push_int<ScriptDataInt>(st->stack, 0);
    }
    else
    {
        push_int<ScriptDataInt>(st->stack, 1);
    }

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getitem(ScriptState *st)
{
    ItemNameId nameid;
    int amount;
    dumb_ptr<map_session_data> sd;
    struct script_data *data;

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    data = &AARG(0);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            nameid = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        nameid = wrap<ItemNameId>(conv_num(st, data));

    if ((amount =
         conv_num(st, &AARG(1))) <= 0)
    {
        return;               //return if amount <=0, skip the useles iteration
    }

    if (nameid)
    {
        Item item_tmp {};
        item_tmp.nameid = nameid;
        if (HARG(3))    //アイテムを指定したIDに渡す | Pass an item to the specified ID
            sd = map_id2sd(wrap<BlockId>(conv_num(st, &AARG(3))));
        if (sd == nullptr)         //アイテムを渡す相手がいなかったらお帰り | If you don't have anyone to give the item to, go home
            return;
        PickupFail flag;
        if ((flag = pc_additem(sd, &item_tmp, amount)) != PickupFail::OKAY)
        {
            clif_additem(sd, IOff0::from(0), 0, flag);
            map_addflooritem(&item_tmp, amount,
                    sd->bl_m, sd->bl_x, sd->bl_y,
                    nullptr, nullptr, nullptr);
        }
    }

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_makeitem(ScriptState *st)
{
    ItemNameId nameid;
    int amount;
    int x, y;
    dumb_ptr<map_session_data> sd;
    struct script_data *data;

    sd = script_rid2sd(st);

    data = &AARG(0);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            nameid = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        nameid = wrap<ItemNameId>(conv_num(st, data));

    amount = conv_num(st, &AARG(1));
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(2))));
    x = conv_num(st, &AARG(3));
    y = conv_num(st, &AARG(4));

    P<map_local> m = ((sd && mapname == MOB_THIS_MAP)
            ? sd->bl_m
            : TRY_UNWRAP(map_mapname2mapid(mapname), return));

    if (nameid)
    {
        Item item_tmp {};
        item_tmp.nameid = nameid;

        map_addflooritem(&item_tmp, amount, m, x, y, nullptr, nullptr, nullptr);
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_delitem(ScriptState *st)
{
    ItemNameId nameid;
    int amount;
    dumb_ptr<map_session_data> sd;
    struct script_data *data;

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    data = &AARG(0);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            nameid = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        nameid = wrap<ItemNameId>(conv_num(st, data));

    amount = conv_num(st, &AARG(1));

    if (!nameid || amount <= 0)
    {
        //By Lupus. Don't run FOR if you've got the wrong item ID or amount<=0
        return;
    }

    for (IOff0 i : IOff0::iter())
    {
        if (sd->status.inventory[i].nameid == nameid)
        {
            if (sd->status.inventory[i].amount >= amount)
            {
                pc_delitem(sd, i, amount, 0);
                break;
            }
            else
            {
                amount -= sd->status.inventory[i].amount;
                if (amount == 0)
                    amount = sd->status.inventory[i].amount;
                pc_delitem(sd, i, amount, 0);
                break;
            }
        }
    }

}

static
void builtin_getversion(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, unwrap<ClientVersion>(sd->client_version));
}

/*==========================================
 * キャラ関係のID取得
 * Acquisition of ID related to characters
 *------------------------------------------
 */
static
void builtin_getcharid(ScriptState *st)
{
    int num;
    dumb_ptr<map_session_data> sd;

    num = conv_num(st, &AARG(0));
    if (HARG(1))
        sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARG(1)))));
    else
        sd = script_rid2sd(st);

    if (sd == nullptr) {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    }

    if (num == 0)
        push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status_key.char_id));
    if (num == 1)
        push_int<ScriptDataInt>(st->stack, unwrap<PartyId>(sd->status.party_id));
    if (num == 2)
        push_int<ScriptDataInt>(st->stack, 0/*guild_id*/);
    if (num == 3)
        push_int<ScriptDataInt>(st->stack, unwrap<AccountId>(sd->status_key.account_id));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getnpcid(ScriptState *st)
{
    dumb_ptr<npc_data> nd;

    if (HARG(0))
        nd = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
    else
        nd = map_id_is_npc(st->oid);
    if (nd == nullptr)
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    }

    push_int<ScriptDataInt>(st->stack, unwrap<BlockId>(nd->bl_id));
}

/*==========================================
 * 指定IDのPT名取得
 * Obtaining PT name of specified ID
 *------------------------------------------
 */
static
RString builtin_getpartyname_sub(PartyId party_id)
{
    Option<PartyPair> p = party_search(party_id);

    return p.pmd_pget(&PartyMost::name).copy_or(PartyName());
}

/*==========================================
 * キャラクタの名前
 * Character's name
 *------------------------------------------
 */
static
void builtin_strcharinfo(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;
    int num;

    if (HARG(1))    //指定したキャラを状態異常にする | Make the specified character abnormal
        sd = map_id_is_player(wrap<BlockId>(conv_num(st, &AARG(1))));
    else
        sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");

    num = conv_num(st, &AARG(0));
    if (num == 0)
    {
        RString buf = sd->status_key.name.to__actual();
        push_str<ScriptDataStr>(st->stack, buf);
    }
    if (num == 1)
    {
        RString buf = builtin_getpartyname_sub(sd->status.party_id);
        if (buf)
            push_str<ScriptDataStr>(st->stack, buf);
        else
            push_str<ScriptDataStr>(st->stack, ""_s);
    }
    if (num == 2)
    {
        // was: guild name
        push_str<ScriptDataStr>(st->stack, ""_s);
    }

}

// indexed by the equip_* in db/const.txt
// TODO change to use EQUIP
static
Array<EPOS, 11> equip //=
{{
    EPOS::HAT,
    EPOS::MISC1,
    EPOS::SHIELD,
    EPOS::WEAPON,
    EPOS::GLOVES,
    EPOS::SHOES,
    EPOS::CAPE,
    EPOS::MISC2,
    EPOS::TORSO,
    EPOS::LEGS,
    EPOS::ARROW,
}};

/*==========================================
 * GetEquipID(Pos);     Pos: 1-10
 *------------------------------------------
 */
static
void builtin_getequipid(ScriptState *st)
{
    int num;
    dumb_ptr<map_session_data> sd;

    if (HARG(1))
        sd = map_nick2sd(stringish<CharName>(ZString(conv_str(st, &AARG(1)))));
    else
        sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");
    num = conv_num(st, &AARG(0));
    IOff0 i = pc_checkequip(sd, equip[num - 1]);
    if (i.ok())
    {
        Option<P<struct item_data>> item_ = sd->inventory_data[i];
        OMATCH_BEGIN (item_)
        {
            OMATCH_CASE_SOME (item)
            {
                push_int<ScriptDataInt>(st->stack, unwrap<ItemNameId>(item->nameid));
            }
            OMATCH_CASE_NONE ()
            {
                push_int<ScriptDataInt>(st->stack, 0);
            }
        }
        OMATCH_END ();
    }
    else
    {
        push_int<ScriptDataInt>(st->stack, -1);
    }
}

/*==========================================
 * freeloop
 *------------------------------------------
 */
static
void builtin_freeloop(ScriptState *st)
{
    int num;
    num = conv_num(st, &AARG(0));
    if(num == 1)
    {
        st->freeloop = 1;
    }
    else
    {
        st->freeloop = 0;
    }
}

/*==========================================
 * 装備品による能力値ボーナス
 * Ability bonuses for equipment
 *------------------------------------------
 */
static
void builtin_bonus(ScriptState *st)
{
    SP type;
    if (auto *u = AARG(0).get_if<ScriptDataParam>())
        type = u->reg.sp();
    else
        type = SP(conv_num(st, &AARG(0)));
    int val = conv_num(st, &AARG(1));
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    pc_bonus(sd, type, val);

}

/*==========================================
 * 装備品による能力値ボーナス
 * Ability bonuses for equipment
 *------------------------------------------
 */
static
void builtin_bonus2(ScriptState *st)
{
    SP type;
    if (auto *u = AARG(0).get_if<ScriptDataParam>())
        type = u->reg.sp();
    else
        type = SP(conv_num(st, &AARG(0)));
    int type2 = conv_num(st, &AARG(1));
    int val = conv_num(st, &AARG(2));
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    pc_bonus2(sd, type, type2, val);

}

/*==========================================
 * スキル所得
 * Skill Income
 *------------------------------------------
 */
static
void builtin_skill(ScriptState *st)
{
    int level, flag = 1;
    dumb_ptr<map_session_data> sd;

    SkillID id = SkillID(conv_num(st, &AARG(0)));
    level = conv_num(st, &AARG(1));
    if (HARG(2))
        flag = conv_num(st, &AARG(2));
    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    pc_skill(sd, id, level, flag);
    clif_skillinfoblock(sd);

}

/*==========================================
 * [Fate] Sets the skill level permanently
 *------------------------------------------
 */
static
void builtin_setskill(ScriptState *st)
{
    int level;
    dumb_ptr<map_session_data> sd;

    SkillID id = static_cast<SkillID>(conv_num(st, &AARG(0)));
    level = conv_num(st, &AARG(1));
    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    level = std::min(level, MAX_SKILL_LEVEL);
    level = std::max(level, 0);
    sd->status.skill[id].lv = level;
    clif_skillinfoblock(sd);
}

/*==========================================
 * スキルレベル所得
 * Skill Level Income
 *------------------------------------------
 */
static
void builtin_getskilllv(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    SkillID id = SkillID(conv_num(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, pc_checkskill(sd, id));
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_overrideattack(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (HARG(0))
    {
        interval_t attack_delay = static_cast<interval_t>(conv_num(st, &AARG(0)));
        int attack_range = conv_num(st, &AARG(1));
        StatusChange icon = StatusChange(conv_num(st, &AARG(2)));
        ItemNameId look = wrap<ItemNameId>(static_cast<uint16_t>(conv_num(st, &AARG(3))));
        ZString event_ = ZString(conv_str(st, &AARG(4)));

        NpcEvent event;
        extract(event_, &event);

        sd->attack_spell_override = st->oid;
        sd->attack_spell_charges = HARG(5) ? conv_num(st, &AARG(5)) : 1;
        sd->magic_attack = event;
        pc_set_weapon_icon(sd, 1, icon, look);
        pc_set_attack_info(sd, attack_delay, attack_range);
    }
    else
    {
        // explicit discharge
        sd->attack_spell_override = BlockId();
        pc_set_weapon_icon(sd, 0, StatusChange::ZERO, ItemNameId());
        pc_set_attack_info(sd, interval_t::zero(), 0);
        pc_calcstatus(sd, 0);
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getgmlevel(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    push_int<ScriptDataInt>(st->stack, pc_isGM(sd).get_all_bits());
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_end(ScriptState *st)
{
    if (st->defsp >= 1 && st->stack->stack_datav[st->defsp - 1].is<ScriptDataRetInfo>())
    {
        dumb_ptr<npc_data> nd = map_id_is_npc(st->oid);
        if(nd)
            PRINTF("Deprecated: close in a callfunc or callsub! @ %s\n"_fmt, nd->name);
        else
            PRINTF("Deprecated: close in a callfunc or callsub! (no npc)\n"_fmt);
    }
    st->state = ScriptEndState::END;
}

/*==========================================
 * [Freeyorp] Return the current opt2
 *------------------------------------------
 */
static
void builtin_getopt2(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;

    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    push_int<ScriptDataInt>(st->stack, static_cast<uint16_t>(sd->opt2));

}

/*==========================================
 * [Freeyorp] Sets opt2
 *------------------------------------------
 */
static
void builtin_setopt2(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;

    Opt2 new_opt2 = Opt2(conv_num(st, &AARG(0)));
    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (new_opt2 == sd->opt2)
        return;
    sd->opt2 = new_opt2;
    clif_changeoption(sd);
    pc_calcstatus(sd, 0);

}

/*==========================================
 * セーブポイントの保存
 * Saving savepoints
 *------------------------------------------
 */
static
void builtin_savepoint(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    int x, y;
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    script_nullpo_end(sd, "player not found");

    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));

    pc_setsavepoint(script_rid2sd(st), str, x, y);
}

/*==========================================
 * gettimetick(type)
 *
 * type The type of time measurement.
 *  Specify 0 for the system tick, 1 for
 *  seconds elapsed today, or 2 for seconds
 *  since Unix epoch. Defaults to 0 for any
 *  other value.
 *------------------------------------------
 */
static
void builtin_gettimetick(ScriptState *st)   /* Asgard Version */
{
    int type;
    type = conv_num(st, &AARG(0));

    switch (type)
    {
        /* Number of seconds elapsed today(0-86399, 00:00:00-23:59:59). */
        case 1:
        {
            struct tm t = TimeT::now();
            push_int<ScriptDataInt>(st->stack,
                    t.tm_hour * 3600 + t.tm_min * 60 + t.tm_sec);
            break;
        }
        /* Seconds since Unix epoch. */
        case 2:
            push_int<ScriptDataInt>(st->stack, static_cast<time_t>(TimeT::now()));
            break;
        /* System tick(unsigned int, and yes, it will wrap). */
        case 0:
        default:
            push_int<ScriptDataInt>(st->stack, gettick().time_since_epoch().count());
            break;
    }
}

/*==========================================
 * GetTime(Type);
 * 1: Sec     2: Min     3: Hour
 * 4: WeekDay     5: MonthDay     6: Month
 * 7: Year
 *------------------------------------------
 */
static
void builtin_gettime(ScriptState *st)   /* Asgard Version */
{
    int type = conv_num(st, &AARG(0));

    struct tm t = TimeT::now();

    switch (type)
    {
        case 1:                //Sec(0~59)
            push_int<ScriptDataInt>(st->stack, t.tm_sec);
            break;
        case 2:                //Min(0~59)
            push_int<ScriptDataInt>(st->stack, t.tm_min);
            break;
        case 3:                //Hour(0~23)
            push_int<ScriptDataInt>(st->stack, t.tm_hour);
            break;
        case 4:                //WeekDay(0~6)
            push_int<ScriptDataInt>(st->stack, t.tm_wday);
            break;
        case 5:                //MonthDay(01~31)
            push_int<ScriptDataInt>(st->stack, t.tm_mday);
            break;
        case 6:                //Month(01~12)
            push_int<ScriptDataInt>(st->stack, t.tm_mon + 1);
            break;
        case 7:                //Year(20xx)
            push_int<ScriptDataInt>(st->stack, t.tm_year + 1900);
            break;
        default:               //(format error)
            push_int<ScriptDataInt>(st->stack, -1);
            break;
    }
}

/*==========================================
 * カプラ倉庫を開く
 * Opening a coupler warehouse
 *------------------------------------------
 */
static
void builtin_openstorage(ScriptState *st)
{
//  int sync = 0;
//  if (st->end >= 3) sync = conv_num(st,& (st->stack->stack_data[st->start+2]));
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

//  if (sync) {
    st->state = ScriptEndState::STOP;
    sd->npc_flags.storage = 1;
//  } else st->state = ScriptEndState::END;

    storage_storageopen(sd);
}

/*==========================================
 * NPCで経験値上げる
 * Increase experience with NPCs
 *------------------------------------------
 */
static
void builtin_getexp(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    int base = 0, job = 0;
    script_nullpo_end(sd, "player not found");

    base = conv_num(st, &AARG(0));
    job = conv_num(st, &AARG(1));
    if (base < 0 || job < 0)
        return;
    if (sd)
        pc_gainexp_reason(sd, base, job, PC_GAINEXP_REASON::SCRIPT);

}

/*==========================================
 * Returns attributes of a monster
 * return value -1 = mob not found
 *------------------------------------------
 */
static
void builtin_mobinfo(ScriptState *st)
{
    Species mob_id = wrap<Species>(conv_num(st, &AARG(0)));
    MobInfo request = MobInfo(conv_num(st, &AARG(1)));
    int info = 0;
    AString info_str;
    char mode = 0; // 0 = int, 1 = str

    if (mobdb_checkid(mob_id) == Species())
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    }

    switch (request)
    {
        case MobInfo::ID:
            info = unwrap<Species>(mob_id);
            break;
        case MobInfo::ENG_NAME:
            info_str = get_mob_db(mob_id).name;
            mode = 1;
            break;
        case MobInfo::JAP_NAME:
            info_str = get_mob_db(mob_id).jname;
            mode = 1;
            break;
        case MobInfo::LVL:
            info = get_mob_db(mob_id).lv;
            break;
        case MobInfo::HP:
            info = get_mob_db(mob_id).max_hp;
            break;
        case MobInfo::SP:
            info = get_mob_db(mob_id).max_sp;
            break;
        case MobInfo::BASE_EXP:
            info = get_mob_db(mob_id).base_exp;
            break;
        case MobInfo::JOB_EXP:
            info = get_mob_db(mob_id).job_exp;
            break;
        case MobInfo::RANGE1:
            info = get_mob_db(mob_id).range;
            break;
        case MobInfo::ATK1:
            info = get_mob_db(mob_id).atk1;
            break;
        case MobInfo::ATK2:
            info = get_mob_db(mob_id).atk2;
            break;
        case MobInfo::DEF:
            info = get_mob_db(mob_id).def;
            break;
        case MobInfo::MDEF:
            info = get_mob_db(mob_id).mdef;
            break;
        case MobInfo::STR:
            info = get_mob_db(mob_id).attrs[ATTR::STR];
            break;
        case MobInfo::AGI:
            info = get_mob_db(mob_id).attrs[ATTR::AGI];
            break;
        case MobInfo::VIT:
            info = get_mob_db(mob_id).attrs[ATTR::VIT];
            break;
        case MobInfo::INT:
            info = get_mob_db(mob_id).attrs[ATTR::INT];
            break;
        case MobInfo::DEX:
            info = get_mob_db(mob_id).attrs[ATTR::DEX];
            break;
        case MobInfo::LUK:
            info = get_mob_db(mob_id).attrs[ATTR::LUK];
            break;
        case MobInfo::RANGE2:
            info = get_mob_db(mob_id).range2;
            break;
        case MobInfo::RANGE3:
            info = get_mob_db(mob_id).range3;
            break;
        case MobInfo::SCALE:
            info = get_mob_db(mob_id).size;
            break;
        case MobInfo::RACE:
            info = int(get_mob_db(mob_id).race);
            break;
        case MobInfo::ELEMENT:
            info = int(get_mob_db(mob_id).element.element);
            break;
        case MobInfo::ELEMENT_LVL:
            info = get_mob_db(mob_id).element.level;
            break;
        case MobInfo::MODE:
            info = int(get_mob_db(mob_id).mode);
            break;
        case MobInfo::SPEED:
            info = get_mob_db(mob_id).speed.count();
            break;
        case MobInfo::ADELAY:
            info = get_mob_db(mob_id).adelay.count();
            break;
        case MobInfo::AMOTION:
            info = get_mob_db(mob_id).amotion.count();
            break;
        case MobInfo::DMOTION:
            info = get_mob_db(mob_id).dmotion.count();
            break;
        case MobInfo::MUTATION_NUM:
            info = get_mob_db(mob_id).mutations_nr;
            break;
        case MobInfo::MUTATION_POWER:
            info = get_mob_db(mob_id).mutation_power;
            break;
        case MobInfo::DROPID1:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[0].nameid);
            break;
        case MobInfo::DROPNAME1:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[0].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT1:
            info = get_mob_db(mob_id).dropitem[0].p.num;
            break;
        case MobInfo::DROPID2:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[1].nameid);
            break;
        case MobInfo::DROPNAME2:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[1].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT2:
            info = get_mob_db(mob_id).dropitem[1].p.num;
            break;
        case MobInfo::DROPID3:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[2].nameid);
            break;
        case MobInfo::DROPNAME3:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[2].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT3:
            info = get_mob_db(mob_id).dropitem[2].p.num;
            break;
        case MobInfo::DROPID4:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[3].nameid);
            break;
        case MobInfo::DROPNAME4:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[3].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT4:
            info = get_mob_db(mob_id).dropitem[3].p.num;
            break;
        case MobInfo::DROPID5:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[4].nameid);
            break;
        case MobInfo::DROPNAME5:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[4].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT5:
            info = get_mob_db(mob_id).dropitem[4].p.num;
            break;
        case MobInfo::DROPID6:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[5].nameid);
            break;
        case MobInfo::DROPNAME6:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[5].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT6:
            info = get_mob_db(mob_id).dropitem[5].p.num;
            break;
        case MobInfo::DROPID7:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[6].nameid);
            break;
        case MobInfo::DROPNAME7:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[6].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT7:
            info = get_mob_db(mob_id).dropitem[6].p.num;
            break;
        case MobInfo::DROPID8:
            info = unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[7].nameid);
            break;
        case MobInfo::DROPNAME8:
            {
                Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[7].nameid));
                info_str = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
                mode = 1;
            }
            break;
        case MobInfo::DROPPERCENT8:
            info = get_mob_db(mob_id).dropitem[7].p.num;
            break;
        default:
            PRINTF("builtin_mobinfo: unknown request\n"_fmt);
            push_int<ScriptDataInt>(st->stack, -1);
            return;
            break;
    }
    if (!mode)
        push_int<ScriptDataInt>(st->stack, info);
    else
        push_str<ScriptDataStr>(st->stack, info_str);
}

/*==========================================
 * Returns drops of a monster to an array
 * return values:
 * 0 = mob not found or error
 * 1 = mob found and has drops
 * 2 = mob found and has no drops
 *------------------------------------------
 */
static
void builtin_mobinfo_droparrays(ScriptState *st)
{
    dumb_ptr<block_list> bl = nullptr;

    Species mob_id = wrap<Species>(conv_num(st, &AARG(0)));

    MobInfo_DropArrays request = MobInfo_DropArrays(conv_num(st, &AARG(1)));

    SIR reg = AARG(2).get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());

    char prefix = name.front();
    char postfix = name.back();
    
    int status = 0; // 0 = mob not found or error, 1 = mob found and has drops, 2 = mob found and has no drops

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_mobinfo_droparrays: illegal scope!\n"_fmt);
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    if (prefix == '.' && !name.startswith(".@"_s))
        bl = map_id_is_npc(st->oid);
    else if (prefix != '$' && !name.startswith(".@"_s))
    {
        bl = map_id_is_player(st->rid);
        script_nullpo_end(bl, "player not found");
    }

    switch (request)
    {
        case MobInfo_DropArrays::IDS:
            if (postfix == '$')
            {
                PRINTF("builtin_mobinfo_droparrays: wrong array type for ID's (Int expected but String found)!\n"_fmt);
                push_int<ScriptDataInt>(st->stack, 0);
                return;
            }
            break;
        case MobInfo_DropArrays::NAMES:
            if (postfix != '$')
            {
                PRINTF("builtin_mobinfo_droparrays: wrong array type for Names (String expected but Int found)!\n"_fmt);
                push_int<ScriptDataInt>(st->stack, 0);
                return;
            }
            break;
        case MobInfo_DropArrays::PERCENTS:
            if (postfix == '$')
            {
                PRINTF("builtin_mobinfo_droparrays: wrong array type for Percents (Int expected but String found)!\n"_fmt);
                push_int<ScriptDataInt>(st->stack, 0);
                return;
            }
            break;
        default:
            PRINTF("builtin_mobinfo_droparrays: unknown request\n"_fmt);
            push_int<ScriptDataInt>(st->stack, 0);
            return;
            break;
    }

    if (mobdb_checkid(mob_id) == Species())
    {
        push_int<ScriptDataInt>(st->stack, status);
        return;
    }
    
    for (int i = 0; i < MaxDrops; ++i)
        if (get_mob_db(mob_id).dropitem[i].nameid)
        {
            status = 1;
            switch (request)
            {
                case MobInfo_DropArrays::IDS:
                    if (name.startswith(".@"_s))
                        {
                            struct script_data vd = script_data(ScriptDataInt{unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[i].nameid)});
                            set_scope_reg(st, reg.iplus(i), &vd);
                        }
                    else
                        set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), unwrap<ItemNameId>(get_mob_db(mob_id).dropitem[i].nameid));
                    break;
                case MobInfo_DropArrays::NAMES:
                    {
                        Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid));
                        RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));

                        if (name.startswith(".@"_s))
                        {
                            struct script_data vd = script_data(ScriptDataStr{item_name});
                            set_scope_reg(st, reg.iplus(i), &vd);
                        }
                        else
                            set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), item_name);
                        break;
                    }
                case MobInfo_DropArrays::PERCENTS:
                    if (name.startswith(".@"_s))
                        {
                            struct script_data vd = script_data(ScriptDataInt{get_mob_db(mob_id).dropitem[i].p.num});
                            set_scope_reg(st, reg.iplus(i), &vd);
                        }
                    else
                        set_reg(bl, VariableCode::VARIABLE, reg.iplus(i), get_mob_db(mob_id).dropitem[i].p.num);
                    break;
            }
        }
        else
        {
            if (i == 0)
                status = 2;
            break;
        }

    push_int<ScriptDataInt>(st->stack, status);
}

/*==========================================
 * Returns drops of a monster to standardized arrays
 * return values:
 * 0 = mob not found
 * 1 = mob found and has drops
 * 2 = mob found and has no drops
 *------------------------------------------
 */
static
void builtin_getmobdrops(ScriptState *st)
{
    dumb_ptr<block_list> bl = nullptr;

    Species mob_id = wrap<Species>(conv_num(st, &AARG(0)));

    int status = 0; // 0 = mob not found, 1 = mob found and has drops, 2 = mob found and has no drops
    int i = 0;

    if (mobdb_checkid(mob_id) == Species())
    {
        push_int<ScriptDataInt>(st->stack, status);
        return;
    }

    status = 1;

    for (; i < MaxDrops; ++i)
        if (get_mob_db(mob_id).dropitem[i].nameid)
        {
            set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_item"_s), i), get_mob_db(mob_id).dropitem[i].p.num);

            Option<P<struct item_data>> i_data = Some(itemdb_search(get_mob_db(mob_id).dropitem[i].nameid));
            RString item_name = i_data.pmd_pget(&item_data::name).copy_or(stringish<ItemName>(""_s));
            set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_name$"_s), i), item_name);

            set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_rate"_s), i), get_mob_db(mob_id).dropitem[i].p.num);
        }
        else
        {
            if (i == 0)
                status = 2;
            break;
        }

    if (status == 1)
        set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_count"_s), 0), i);
    else
        set_reg(bl, VariableCode::VARIABLE, SIR::from(variable_names.intern("$@MobDrop_count"_s), 0), 0);

    push_int<ScriptDataInt>(st->stack, status);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_summon(ScriptState *st)
{
    NpcEvent event;
    MapName map = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    int x = conv_num(st, &AARG(1));
    int y = conv_num(st, &AARG(2));
    dumb_ptr<block_list> owner_e = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(3))));
    dumb_ptr<map_session_data> owner = nullptr;
    MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(4))));
    Species monster_id = wrap<Species>(conv_num(st, &AARG(5)));
    MonsterAttitude monster_attitude = static_cast<MonsterAttitude>(conv_num(st, &AARG(6)));
    interval_t lifespan = static_cast<interval_t>(conv_num(st, &AARG(7)));
    if (HARG(8))
        extract(ZString(conv_str(st, &AARG(8))), &event);

    if (!owner_e)
    {
        PRINTF("builtin_summon: bad owner\n"_fmt);
        return;
    }

    if (monster_attitude == MonsterAttitude::SERVANT
        && owner_e->bl_type == BL::PC)
        owner = owner_e->is_player(); // XXX in the future this should also work with mobs as owner

    if (!str)
        str = MobName();
    BlockId mob_id = mob_once_spawn(owner, map, x, y, str, monster_id, 1, event);
    dumb_ptr<mob_data> mob = map_id_is_mob(mob_id);

    if (mob)
    {
        mob->mode = get_mob_db(monster_id).mode;

        switch (monster_attitude)
        {
            case MonsterAttitude::SERVANT:
                mob->state.special_mob_ai = 1;
                mob->mode |= MobMode::AGGRESSIVE;
                break;

            case MonsterAttitude::FRIENDLY:
                mob->mode = MobMode::CAN_ATTACK | (mob->mode & MobMode::CAN_MOVE);
                break;

            case MonsterAttitude::HOSTILE:
                mob->mode = MobMode::CAN_ATTACK | MobMode::AGGRESSIVE | (mob->mode & MobMode::CAN_MOVE);
                if (owner)
                {
                    mob->target_id = owner->bl_id;
                    mob->attacked_id = owner->bl_id;
                }
                break;

            case MonsterAttitude::FROZEN:
                mob->mode = MobMode::ZERO;
                break;
        }

        mob->mode |=
            MobMode::SUMMONED | MobMode::TURNS_AGAINST_BAD_MASTER;

        mob->deletetimer = Timer(gettick() + lifespan,
                std::bind(mob_timer_delete, ph::_1, ph::_2,
                    mob_id));

        if (owner)
        {
            mob->master_id = owner->bl_id;
            mob->master_dist = 6;
        }
    }
}

/*==========================================
 * モンスター発生
 * Monster Outbreak
 *------------------------------------------
 */
static
void builtin_monster(ScriptState *st)
{
    Species mob_class;
    int amount, x, y;
    NpcEvent event;

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));
    MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(3))));
    mob_class = wrap<Species>(conv_num(st, &AARG(4)));
    amount = conv_num(st, &AARG(5));
    if (HARG(6))
        extract(ZString(conv_str(st, &AARG(6))), &event);

    mob_once_spawn(map_id2sd(st->rid), mapname, x, y, str, mob_class, amount,
            event);
}

/*==========================================
 * モンスター発生
 * Monster Outbreak
 *------------------------------------------
 */
static
void builtin_areamonster(ScriptState *st)
{
    Species mob_class;
    int amount, x0, y0, x1, y1;
    NpcEvent event;

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x0 = conv_num(st, &AARG(1));
    y0 = conv_num(st, &AARG(2));
    x1 = conv_num(st, &AARG(3));
    y1 = conv_num(st, &AARG(4));
    MobName str = stringish<MobName>(ZString(conv_str(st, &AARG(5))));
    mob_class = wrap<Species>(conv_num(st, &AARG(6)));
    amount = conv_num(st, &AARG(7));
    if (HARG(8))
        extract(ZString(conv_str(st, &AARG(8))), &event);

    mob_once_spawn_area(map_id2sd(st->rid), mapname, x0, y0, x1, y1, str, mob_class,
            amount, event);
}

/*==========================================
 * モンスター削除
 * Monster Removal
 *------------------------------------------
 */
static
void builtin_killmonster_sub(dumb_ptr<block_list> bl, NpcEvent event)
{
    dumb_ptr<mob_data> md = bl->is_mob();
    if (event)
    {
        if (event == md->npc_event)
            mob_delete(md);
        return;
    }
    else if (!event)
    {
        if (md->spawn.delay1 == static_cast<interval_t>(-1)
            && md->spawn.delay2 == static_cast<interval_t>(-1))
            mob_delete(md);
        return;
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_killmonster(ScriptState *st)
{
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    ZString event_ = ZString(conv_str(st, &AARG(1)));
    NpcEvent event;
    if (event_ != "All"_s)
        extract(event_, &event);

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
    map_foreachinarea(std::bind(builtin_killmonster_sub, ph::_1, event),
            m,
            0, 0,
            m->xs, m->ys,
            BL::MOB);
}

/*==========================================
 * NPC主体イベント実行
 * NPC-Driven Event Execution
 *------------------------------------------
 */
static
void builtin_donpcevent(ScriptState *st)
{
    ZString event_ = ZString(conv_str(st, &AARG(0)));
    NpcEvent event;
    extract(event_, &event);
    npc_event_do(event);
}

/*==========================================
 * イベントタイマー追加
 * Add Event Timer
 *------------------------------------------
 */
static
void builtin_addtimer(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;
    interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
    ZString event_ = ZString(conv_str(st, &AARG(1)));
    NpcEvent event;
    extract(event_, &event);

    if (HARG(2))
        sd = map_id_is_player(wrap<BlockId>(conv_num(st, &AARG(2))));
    else if (st->rid)
        sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");

    pc_addeventtimer(sd, tick, event);
}

/*==========================================
 * NPCイベントタイマー追加
 * Add NPC Event Timer
 *------------------------------------------
 */
static
void builtin_addnpctimer(ScriptState *st)
{
    interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
    ZString event_ = ZString(conv_str(st, &AARG(1)));
    NpcEvent event;
    extract(event_, &event);
    npc_addeventtimer(npc_name2id(event.npc), tick, event);
}

/*==========================================
 * NPCタイマー初期化
 * NPC Timer Initialization
 *------------------------------------------
 */
static
void builtin_initnpctimer(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    if (HARG(0))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
    else
        nd_ = map_id_is_npc(st->oid);
    script_nullpo_end(nd_, "no npc");
    assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
    dumb_ptr<npc_data_script> nd = nd_->is_script();

    npc_settimerevent_tick(nd, interval_t::zero());
    npc_timerevent_start(nd);
}

/*==========================================
 * NPCタイマー開始
 * NPC Timer Start
 *------------------------------------------
 */
static
void builtin_startnpctimer(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    if (HARG(0))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
    else
        nd_ = map_id_is_npc(st->oid);
    script_nullpo_end(nd_, "no npc");
    assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
    dumb_ptr<npc_data_script> nd = nd_->is_script();

    npc_timerevent_start(nd);
}

/*==========================================
 * NPCタイマー停止
 * NPC Timer Stop
 *------------------------------------------
 */
static
void builtin_stopnpctimer(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    if (HARG(0))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
    else
        nd_ = map_id_is_npc(st->oid);
    script_nullpo_end(nd_, "no npc");
    assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
    dumb_ptr<npc_data_script> nd = nd_->is_script();

    npc_timerevent_stop(nd);
}

/*==========================================
 * NPCタイマー情報所得
 * NPC Timer Information Income
 *------------------------------------------
 */
static
void builtin_getnpctimer(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    int type = conv_num(st, &AARG(0));
    int val = 0;
    if (HARG(1))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
    else
        nd_ = map_id_is_npc(st->oid);
    script_nullpo_end(nd_, "no npc");
    assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
    dumb_ptr<npc_data_script> nd = nd_->is_script();

    switch (type)
    {
        case 0:
            val = npc_gettimerevent_tick(nd).count();
            break;
        case 1:
            val = nd->scr.timer_active;
            break;
        case 2:
            val = nd->scr.timer_eventv.size();
            break;
    }
    push_int<ScriptDataInt>(st->stack, val);
}

/*==========================================
 * NPCタイマー値設定
 * Setting NPC Timer Values
 *------------------------------------------
 */
static
void builtin_setnpctimer(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(0)));
    if (HARG(1))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
    else
        nd_ = map_id_is_npc(st->oid);
    script_nullpo_end(nd_, "no npc");
    assert (nd_ && nd_->npc_subtype == NpcSubtype::SCRIPT);
    dumb_ptr<npc_data_script> nd = nd_->is_script();

    npc_settimerevent_tick(nd, tick);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_npcaction(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    short command = conv_num(st, &AARG(0));
    int id = 0;
    short x = HARG(2) ? conv_num(st, &AARG(2)) : 0;
    short y = HARG(3) ? conv_num(st, &AARG(3)) : 0;
    script_nullpo_end(sd, "player not found");

    if(HARG(1))
    {
        if(command == 2)
        {
            dumb_ptr<npc_data> nd_;
            nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(1)))));
            id = unwrap<BlockId>(nd_->bl_id);
        }
        else
            id = conv_num(st, &AARG(1));
    }

    clif_npc_action(sd, st->oid, command, id, x, y);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_camera(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (HARG(0))
    {
        if (HARG(1) && !HARG(2))
            clif_npc_action(sd, st->oid, 2, 0, conv_num(st, &AARG(0)), conv_num(st, &AARG(1))); // camera to x, y
        else
        {
            dumb_ptr<block_list> bl;
            short x = 0, y = 0;
            bool rel = false;
            if (auto *u = AARG(0).get_if<ScriptDataInt>())
                bl = map_id2bl(wrap<BlockId>(u->numi));
            if (auto *g = AARG(0).get_if<ScriptDataStr>())
            {
                if (g->str == "rid"_s || g->str == "player"_s)
                    bl = sd;
                if (g->str == "relative"_s)
                    rel = true;
                else if (g->str == "oid"_s || g->str == "npc"_s)
                    bl = map_id2bl(st->oid);
                else
                    bl = npc_name2id(stringish<NpcName>(g->str));
            }
            if (HARG(1) && HARG(2))
            {
                x = conv_num(st, &AARG(1));
                y = conv_num(st, &AARG(2));
            }
            if (rel)
                clif_npc_action(sd, st->oid, 4, 0, x, y); // camera relative from current camera
            else
                clif_npc_action(sd, st->oid, 2, unwrap<BlockId>(bl->bl_id), x, y); // camera to actor
        }
    }

    else
        clif_npc_action(sd, st->oid, 3, 0, 0, 0); // return camera
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_setnpcdirection(ScriptState *st)
{
    dumb_ptr<npc_data> nd_;
    DIR dir = static_cast<DIR>(conv_num(st, &AARG(0)));
    bool save = bool(conv_num(st, &AARG(2)));
    DamageType action;

    if (HARG(3))
        nd_ = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(3)))));
    else
        nd_ = map_id_is_npc(st->oid);

    script_nullpo_end(nd_, "no npc");

    if (bool(conv_num(st, &AARG(1))))
        action = DamageType::SIT;
    else
        action = DamageType::STAND;

    if (save)
    {
        nd_->dir = dir;
        nd_->sit = action;
    }

    if (st->rid)
    {
        dumb_ptr<map_session_data> sd = script_rid2sd(st);
        script_nullpo_end(sd, "player not found");
        clif_sitnpc_towards(sd, nd_, action);
        clif_setnpcdirection_towards(sd, nd_, dir);
    }
    else
    {
        clif_sitnpc(nd_, action);
        clif_setnpcdirection(nd_, dir);
    }
}

/*==========================================
 * 天の声アナウンス
 * Voice of Heaven Announcement
 *------------------------------------------
 */
static
void builtin_announce(ScriptState *st)
{
    int flag;
    ZString str = ZString(conv_str(st, &AARG(0)));
    flag = conv_num(st, &AARG(1));

    if (flag & 0x0f)
    {
        dumb_ptr<block_list> bl;
        if (flag & 0x08)
            bl = map_id2bl(st->oid);
        else
            bl = script_rid2sd(st);
        script_nullpo_end(bl, "player not found");
        clif_GMmessage(bl, str, flag);
    }
    else
        intif_GMmessage(str);
}

/*==========================================
 * 天の声アナウンス(特定マップ)
 * Voice of Heaven Announcement (Specific Map)
 *------------------------------------------
 */
static
void builtin_mapannounce_sub(dumb_ptr<block_list> bl, XString str, int flag)
{
    clif_GMmessage(bl, str, flag | 3);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_mapannounce(ScriptState *st)
{
    int flag;

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    ZString str = ZString(conv_str(st, &AARG(1)));
    flag = conv_num(st, &AARG(2));

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
    map_foreachinarea(std::bind(builtin_mapannounce_sub, ph::_1, str, flag & 0x10),
            m,
            0, 0,
            m->xs, m->ys,
            BL::PC);
}

/*==========================================
 * ユーザー数所得
 * User Count
 *------------------------------------------
 */
static
void builtin_getusers(ScriptState *st)
{
    int flag = conv_num(st, &AARG(0));
    dumb_ptr<block_list> bl = map_id2bl((flag & 0x08) ? st->oid : st->rid);
    int val = 0;
    switch (flag & 0x07)
    {
        case 0:
            // FIXME: SIGSEGV error
            //val = bl->bl_m->users;
            PRINTF("Sorry, but getusers(0) is disabled. Please use getmapusers() instead.\n"_fmt);
            break;
        case 1:
            val = map_getusers();
            break;
    }
    push_int<ScriptDataInt>(st->stack, val);
}

/*==========================================
 * マップ指定ユーザー数所得
 * maps Designated User Count
 *------------------------------------------
 */
 static
 void builtin_getareausers_sub(dumb_ptr<block_list> bl, int *users)
 {
     if (bool(bl->is_player()->status.option & Opt0::HIDE))
         return;
     (*users)++;
 }

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getmapusers(ScriptState *st)
{
    int users = 0;
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    });
    map_foreachinarea(std::bind(builtin_getareausers_sub, ph::_1, &users),
            m,
            0, 0,
            m->xs, m->ys,
            BL::PC);
    push_int<ScriptDataInt>(st->stack, users);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_aggravate(ScriptState *st)
{
    dumb_ptr<mob_data> md = map_id_is_mob(wrap<BlockId>(conv_num(st, &AARG(0))));
    if (md)
    {
        dumb_ptr<block_list> target = script_rid2sd(st);
        if (HARG(1))
            target = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));

        nullpo_retv(target);
        mob_aggravate(md, target);
    }
}

/*==========================================
 * エリア指定ユーザー数所得
 * Area Designated User Income
 *------------------------------------------
 */

static
void builtin_getareausers_living_sub(dumb_ptr<block_list> bl, int *users)
{
    if (bool(bl->is_player()->status.option & Opt0::HIDE))
        return;
    if (!pc_isdead(bl->is_player()))
        (*users)++;
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getareausers(ScriptState *st)
{
    int x0, y0, x1, y1, users = 0;
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x0 = conv_num(st, &AARG(1));
    y0 = conv_num(st, &AARG(2));
    x1 = conv_num(st, &AARG(3));
    y1 = conv_num(st, &AARG(4));

    int living = 0;
    if (HARG(5))
    {
        living = conv_num(st, &AARG(5));
    }
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    });
    map_foreachinarea(std::bind(living ? builtin_getareausers_living_sub: builtin_getareausers_sub, ph::_1, &users),
            m,
            x0, y0,
            x1, y1,
            BL::PC);
    push_int<ScriptDataInt>(st->stack, users);
}

/*==========================================
 * エリア指定ドロップアイテム数所得
 * Area Designated Drop Item Number Income
 *------------------------------------------
 */
static
void builtin_getareadropitem_sub(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
{
    dumb_ptr<flooritem_data> drop = bl->is_item();

    if (drop->item_data.nameid == item)
        (*amount) += drop->item_data.amount;

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getareadropitem_sub_anddelete(dumb_ptr<block_list> bl, ItemNameId item, int *amount)
{
    dumb_ptr<flooritem_data> drop = bl->is_item();

    if (drop->item_data.nameid == item)
    {
        (*amount) += drop->item_data.amount;
        clif_clearflooritem(drop, nullptr);
        map_delobject(drop->bl_id, drop->bl_type);
    }
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_getareadropitem(ScriptState *st)
{
    ItemNameId item;
    int x0, y0, x1, y1, amount = 0, delitems = 0;
    struct script_data *data;

    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x0 = conv_num(st, &AARG(1));
    y0 = conv_num(st, &AARG(2));
    x1 = conv_num(st, &AARG(3));
    y1 = conv_num(st, &AARG(4));

    data = &AARG(5);
    get_val(st, data);
    if (data->is<ScriptDataStr>())
    {
        ZString name = ZString(conv_str(st, data));
        Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
        OMATCH_BEGIN_SOME (item_data, item_data_)
        {
            item = item_data->nameid;
        }
        OMATCH_END ();
    }
    else
        item = wrap<ItemNameId>(conv_num(st, data));

    if (HARG(6))
        delitems = conv_num(st, &AARG(6));

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str),
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    });
    if (delitems)
        map_foreachinarea(std::bind(builtin_getareadropitem_sub_anddelete, ph::_1, item, &amount),
                m,
                x0, y0,
                x1, y1,
                BL::ITEM);
    else
        map_foreachinarea(std::bind(builtin_getareadropitem_sub, ph::_1, item, &amount),
                m,
                x0, y0,
                x1, y1,
                BL::ITEM);

    push_int<ScriptDataInt>(st->stack, amount);
}

/*==========================================
 * NPCの有効化
 * Enabling NPCs
 *------------------------------------------
 */
static
void builtin_enablenpc(ScriptState *st)
{
    NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
    npc_enable(str, 1);
}

/*==========================================
 * NPCの無効化
 * Disabling NPCs
 *------------------------------------------
 */
static
void builtin_disablenpc(ScriptState *st)
{
    NpcName str = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
    npc_enable(str, 0);
}

/*==========================================
 * 状態異常にかかる
 * Suffering from an abnormal condition
 *------------------------------------------
 */
static
void builtin_sc_start(ScriptState *st)
{
    dumb_ptr<block_list> bl;
    int val1;
    StatusChange type = static_cast<StatusChange>(conv_num(st, &AARG(0)));
    interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(1)));
    if (tick < 1_s)
        switch (type)
        {
            // all those use ms so this checks for < 1s are not needed on those
            // and it would break the cooldown symbol since many spells have cooldowns less than 1s
            case StatusChange::SC_PHYS_SHIELD:
            case StatusChange::SC_MBARRIER:
            case StatusChange::SC_COOLDOWN:
            case StatusChange::SC_COOLDOWN_MG:
            case StatusChange::SC_COOLDOWN_MT:
            case StatusChange::SC_COOLDOWN_R:
            case StatusChange::SC_COOLDOWN_AR:
            case StatusChange::SC_COOLDOWN_ENCH:
            case StatusChange::SC_COOLDOWN_KOY:
            break;

            default:
            // work around old behaviour of:
            // speed potion
            // atk potion
            // matk potion
            //
            // which used to use seconds
            // all others used milliseconds
            tick *= 1000;
        }
    val1 = conv_num(st, &AARG(2));
    if (HARG(3))    //指定したキャラを状態異常にする | Make the specified character abnormal
        bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(3))));
    else
        bl = map_id2bl(st->rid);
    skill_status_change_start(bl, type, val1, tick);
}

/*==========================================
 * 状態異常が直る
 * Abnormal condition is fixed
 *------------------------------------------
 */
static
void builtin_sc_end(ScriptState *st)
{
    dumb_ptr<block_list> bl;
    StatusChange type = StatusChange(conv_num(st, &AARG(0)));
    if (HARG(1))    //指定したキャラを状態異常にする | Make the specified character abnormal
        bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
    else
        bl = map_id2bl(st->rid);

    skill_status_change_end(bl, type, nullptr);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_sc_check(ScriptState *st)
{
    dumb_ptr<block_list> bl;
    StatusChange type = StatusChange(conv_num(st, &AARG(0)));
    if (HARG(1))    //指定したキャラを状態異常にする | Make the specified character abnormal
        bl = map_id2bl(wrap<BlockId>(conv_num(st, &AARG(1))));
    else
        bl = map_id2bl(st->rid);

    push_int<ScriptDataInt>(st->stack, skill_status_change_active(bl, type));

}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_debugmes(ScriptState *st)
{
    RString mes = conv_str(st, &AARG(0));
    PRINTF("script debug: %d %d: '%s'\n"_fmt,
            st->rid, st->oid, mes);
}

/*==========================================
 * ステータスリセット
 * Status Reset
 *------------------------------------------
 */
static
void builtin_resetstatus(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;
    sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    pc_resetstate(sd);
}

/*==========================================
 * RIDのアタッチ
 * Attach RID
 *------------------------------------------
 */
static
void builtin_attachrid(ScriptState *st)
{
    st->rid = wrap<BlockId>(conv_num(st, &AARG(0)));
    push_int<ScriptDataInt>(st->stack, (map_id2sd(st->rid) != nullptr));
}

/*==========================================
 * RIDのデタッチ
 * Detach RID
 *------------------------------------------
 */
static
void builtin_detachrid(ScriptState *st)
{
    st->rid = BlockId();
}

/*==========================================
 * 存在チェック
 * Existence check
 *------------------------------------------
 */
static
void builtin_isloggedin(ScriptState *st)
{
    push_int<ScriptDataInt>(st->stack,
              map_id2sd(wrap<BlockId>(conv_num(st, &AARG(0)))) != nullptr);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_setmapflag(ScriptState *st)
{
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    int i = conv_num(st, &AARG(1));
    MapFlag mf = map_flag_from_int(i);
    Option<P<map_local>> m_ = map_mapname2mapid(str);
    OMATCH_BEGIN_SOME (m, m_)
    {
        m->flag.set(mf, 1);
    }
    OMATCH_END ();
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_removemapflag(ScriptState *st)
{
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    int i = conv_num(st, &AARG(1));
    MapFlag mf = map_flag_from_int(i);
    Option<P<map_local>> m_ = map_mapname2mapid(str);
    OMATCH_BEGIN_SOME (m, m_)
    {
        m->flag.set(mf, 0);
    }
    OMATCH_END ();
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getmapflag(ScriptState *st)
{
    int r = -1;

    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    int i = conv_num(st, &AARG(1));
    MapFlag mf = map_flag_from_int(i);
    Option<P<map_local>> m_ = map_mapname2mapid(str);
    OMATCH_BEGIN_SOME (m, m_)
    {
        r = m->flag.get(mf);
    }
    OMATCH_END ();

    push_int<ScriptDataInt>(st->stack, r);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_pvpon(ScriptState *st)
{
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return);
    if (!m->flag.get(MapFlag::PVP) && !m->flag.get(MapFlag::NOPVP))
    {
        m->flag.set(MapFlag::PVP, 1);

        if (battle_config.pk_mode)  // disable ranking functions if pk_mode is on [Valaris]
            return;

        for (io::FD i : iter_fds())
        {
            Session *s = get_session(i);
            if (!s)
                continue;
            dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s->session_data.get()));
            if (pl_sd && pl_sd->state.auth)
            {
                if (m == pl_sd->bl_m && !pl_sd->pvp_timer)
                {
                    pl_sd->pvp_timer = Timer(gettick() + 200_ms,
                            std::bind(pc_calc_pvprank_timer, ph::_1, ph::_2,
                                pl_sd->bl_id));
                    //pl_sd->pvp_rank = 0;
                    //pl_sd->pvp_point = 5;
                    clif_map_pvp(pl_sd);
                }
            }
        }
    }
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_pvpoff(ScriptState *st)
{
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(str), return);
    if (m->flag.get(MapFlag::PVP))
    {
        m->flag.set(MapFlag::PVP, 0);

        if (battle_config.pk_mode)  // disable ranking options if pk_mode is on [Valaris]
            return;

        for (io::FD i : iter_fds())
        {
            Session *s = get_session(i);
            if (!s)
                continue;
            dumb_ptr<map_session_data> pl_sd = dumb_ptr<map_session_data>(static_cast<map_session_data *>(s->session_data.get()));
            if (pl_sd && pl_sd->state.auth)
            {
                if (m == pl_sd->bl_m)
                {
                    pl_sd->pvp_timer.cancel();
                    clif_map_pvp(pl_sd);
                }
            }
        }
    }
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_setpvpchannel(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    int flag;
    flag = conv_num(st, &AARG(0));
    if (flag < 1)
        flag = 0;

    script_nullpo_end(sd, "player not found");
    sd->state.pvpchannel = flag;
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getpvpflag(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;
    if (HARG(1))    //指定したキャラを状態異常にする | Make the specified character abnormal
        sd = map_id_is_player(wrap<BlockId>(conv_num(st, &AARG(1))));
    else
        sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");

    int num = conv_num(st, &AARG(0));
    int flag = 0;

    switch (num){
        case 0:
            flag = sd->state.pvpchannel;
            break;
        case 1:
            flag = bool(sd->status.option & Opt0::HIDE);
            break;
    }

    push_int<ScriptDataInt>(st->stack, flag);
}

/*==========================================
 * NPCエモーション
 * NPC Emotion
 *------------------------------------------
 */
static
void builtin_emotion(ScriptState *st)
{
    ZString str;
    dumb_ptr<map_session_data> pl_sd = nullptr;
    int type = conv_num(st, &AARG(0));
    if (HARG(1)) {
        str = ZString(conv_str(st, &AARG(1)));
        CharName player = stringish<CharName>(str);
        pl_sd = map_nick2sd(player);
    }
    if (type < 0 || type > 200)
        return;
    if (pl_sd != nullptr)
        clif_emotion_towards(map_id2bl(st->oid), pl_sd, type);
    else if (st->rid && str == "self"_s)
        clif_emotion(map_id2sd(st->rid), type);
    else
        clif_emotion(map_id2bl(st->oid), type);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_mapwarp(ScriptState *st)   // Added by RoVeRT
{
    int x, y;
    int x0, y0, x1, y1;

    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x0 = 0;
    y0 = 0;
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);
    x1 = m->xs;
    y1 = m->ys;
    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
    x = conv_num(st, &AARG(2));
    y = conv_num(st, &AARG(3));

    map_foreachinarea(std::bind(builtin_areawarp_sub, ph::_1, str, x, y),
            m,
            x0, y0,
            x1, y1,
            BL::PC);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_mobcount_sub(dumb_ptr<block_list> bl, NpcEvent event, int *c)
{
    if (event == bl->is_mob()->npc_event)
        (*c)++;
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_mobcount(ScriptState *st)  // Added by RoVeRT
{
    int c = 0;
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    ZString event_ = ZString(conv_str(st, &AARG(1)));
    NpcEvent event;
    extract(event_, &event);

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname),
    {
        push_int<ScriptDataInt>(st->stack, -1);
        return;
    });
    map_foreachinarea(std::bind(builtin_mobcount_sub, ph::_1, event, &c),
            m,
            0, 0,
            m->xs, m->ys,
            BL::MOB);

    push_int<ScriptDataInt>(st->stack, (c - 1));

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_marriage(ScriptState *st)
{
    CharName partner = stringish<CharName>(ZString(conv_str(st, &AARG(0))));
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    dumb_ptr<map_session_data> p_sd = map_nick2sd(partner);

    if (sd == nullptr || p_sd == nullptr || pc_marriage(sd, p_sd) < 0)
    {
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }
    push_int<ScriptDataInt>(st->stack, 1);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_divorce(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    if (sd == nullptr || pc_divorce(sd) < 0)
    {
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    push_int<ScriptDataInt>(st->stack, 1);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getitemlink(ScriptState *st)
{
    struct script_data *data;
    AString buf;
    data = &AARG(0);
    ZString name = conv_str(st, data);

    Option<P<struct item_data>> item_data_ = itemdb_searchname(name);
    OMATCH_BEGIN (item_data_)
    {
        OMATCH_CASE_SOME (item_data)
        {
            buf = STRPRINTF("@@%d|@@"_fmt, item_data->nameid);
        }
        OMATCH_CASE_NONE ()
        {
            buf = "Unknown Item"_s;
        }
    }
    OMATCH_END ();
    push_str<ScriptDataStr>(st->stack, buf);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getpartnerid2(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, unwrap<CharId>(sd->status.partner_id));
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_chr(ScriptState *st)
{
    const char ascii = conv_num(st, &AARG(0));
    push_str<ScriptDataStr>(st->stack, VString<1>(ascii));
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_ord(ScriptState *st)
{
    const char ascii = conv_str(st, &AARG(0)).front();
    push_int<ScriptDataInt>(st->stack, static_cast<int>(ascii));
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_explode(ScriptState *st)
{
    dumb_ptr<block_list> bl = nullptr;
    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
    ZString name = variable_names.outtern(reg.base());
    const char separator = conv_str(st, &AARG(2)).front();
    RString str = conv_str(st, &AARG(1));
    RString val;
    char prefix = name.front();
    char postfix = name.back();

    if (prefix != '$' && prefix != '@' && prefix != '.')
    {
        PRINTF("builtin_explode: illegal scope!\n"_fmt);
        return;
    }
    if (prefix == '.' && !name.startswith(".@"_s))
        bl = map_id2bl(st->oid)->is_npc();
    else if (prefix != '$' && prefix != '.')
    {
        bl = map_id2bl(st->rid)->is_player();
        script_nullpo_end(bl, "target player not found");
    }


    for (int j = 0; j < 256; j++)
    {
        auto find = std::find(str.begin(), str.end(), separator);
        if (find == str.end())
        {
            if (name.startswith(".@"_s))
            {
                struct script_data vd = script_data(ScriptDataInt{atoi(str.c_str())});
                if (postfix == '$')
                    vd = script_data(ScriptDataStr{str});
                set_scope_reg(st, reg.iplus(j), &vd);
            }
            else if (postfix == '$')
                set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), str);
            else
                set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), atoi(str.c_str()));
            break;
        }
        {
            val = str.xislice_h(find);
            str = str.xislice_t(find + 1);

            if (name.startswith(".@"_s))
            {
                struct script_data vd = script_data(ScriptDataInt{atoi(val.c_str())});
                if (postfix == '$')
                    vd = script_data(ScriptDataStr{val});
                set_scope_reg(st, reg.iplus(j), &vd);
            }
            else if (postfix == '$')
                set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), val);
            else
                set_reg(bl, VariableCode::VARIABLE, reg.iplus(j), atoi(val.c_str()));
        }
    }
}

/*==========================================
 * PCの所持品情報読み取り
 * Read personal belongings information on your PC
 *------------------------------------------
 */
static
void builtin_getinventorylist(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    int j = 0;
    script_nullpo_end(sd, "player not found");

    for (IOff0 i : IOff0::iter())
    {
        if (sd->status.inventory[i].nameid
            && sd->status.inventory[i].amount > 0)
        {
            pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_id"_s), j),
                       unwrap<ItemNameId>(sd->status.inventory[i].nameid));
            pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_amount"_s), j),
                       sd->status.inventory[i].amount);
            pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_equip"_s), j),
                    static_cast<uint16_t>(sd->status.inventory[i].equip));
            j++;
        }
    }
    pc_setreg(sd, SIR::from(variable_names.intern("@inventorylist_count"_s)), j);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getactivatedpoolskilllist(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    SkillID pool_skills[MAX_SKILL_POOL];
    int skill_pool_size = skill_pool(sd, pool_skills);
    int i, count = 0;

    script_nullpo_end(sd, "player not found");

    for (i = 0; i < skill_pool_size; i++)
    {
        SkillID skill_id = pool_skills[i];

        if (sd->status.skill[skill_id].lv)
        {
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
                    static_cast<uint16_t>(skill_id));
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
                    sd->status.skill[skill_id].lv);
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
                    static_cast<uint16_t>(sd->status.skill[skill_id].flags));
            pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
                    skill_name(skill_id));
            ++count;
        }
    }
    pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getunactivatedpoolskilllist(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    int i, count = 0;

    script_nullpo_end(sd, "player not found");

    for (i = 0; i < skill_pool_skills.size(); i++)
    {
        SkillID skill_id = skill_pool_skills[i];

        if (sd->status.skill[skill_id].lv
            && !bool(sd->status.skill[skill_id].flags & SkillFlags::POOL_ACTIVATED))
        {
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_id"_s), count),
                    static_cast<uint16_t>(skill_id));
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_lv"_s), count),
                    sd->status.skill[skill_id].lv);
            pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_flag"_s), count),
                    static_cast<uint16_t>(sd->status.skill[skill_id].flags));
            pc_setregstr(sd, SIR::from(variable_names.intern("@skilllist_name$"_s), count),
                    skill_name(skill_id));
            ++count;
        }
    }
    pc_setreg(sd, SIR::from(variable_names.intern("@skilllist_count"_s)), count);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_poolskill(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    skill_pool_activate(sd, skill_id);
    clif_skillinfoblock(sd);

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_unpoolskill(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    SkillID skill_id = SkillID(conv_num(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    skill_pool_deactivate(sd, skill_id);
    clif_skillinfoblock(sd);

}

/*==========================================
 * NPCから発生するエフェクト
 * Effects generated by NPCs
 * misceffect(effect, [target])
 *
 * effect The effect type/ID.
 * target The player name or being ID on
 *  which to display the effect. If not
 *  specified, it attempts to default to
 *  the current NPC or invoking PC.
 *------------------------------------------
 */
static
void builtin_misceffect(ScriptState *st)
{
    int type;
    BlockId id;
    CharName name;
    dumb_ptr<block_list> bl = nullptr;

    type = conv_num(st, &AARG(0));

    if (HARG(1))
    {
        struct script_data *sdata = &AARG(1);

        get_val(st, sdata);

        if (sdata->is<ScriptDataStr>())
            name = stringish<CharName>(ZString(conv_str(st, sdata)));
        else
            id = wrap<BlockId>(conv_num(st, sdata));
    }

    if (name.to__actual())
    {
        dumb_ptr<map_session_data> sd = map_nick2sd(name);
        if (sd)
            bl = sd;
    }
    else if (id)
        bl = map_id2bl(id);
    else if (st->oid)
        bl = map_id2bl(st->oid);
    else
    {
        dumb_ptr<map_session_data> sd = script_rid2sd(st);
        if (sd)
            bl = sd;
    }

    if (bl)
        clif_misceffect(bl, type);

}

/*==========================================
 * Special effects [Valaris]
 *------------------------------------------
 */
static
void builtin_specialeffect(ScriptState *st)
{
    dumb_ptr<block_list> bl = map_id2bl(st->oid);

    if (bl == nullptr)
        return;

    clif_specialeffect(bl,
                        conv_num(st,
                                  &AARG(0)),
                        0);

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_specialeffect2(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    nullpo_retv(sd);

    clif_specialeffect(sd,
                        conv_num(st,
                                  &AARG(0)),
                        0);

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_get(ScriptState *st)
{
    BlockId id;
    dumb_ptr<block_list> bl = nullptr;
    if (auto *u = AARG(0).get_if<ScriptDataParam>())
    {
        SIR reg = u->reg;
        struct script_data *sdata = &AARG(1);
        get_val(st, sdata);
        CharName name;
        if (sdata->is<ScriptDataStr>())
        {
            name = stringish<CharName>(ZString(conv_str(st, sdata)));
            if (name.to__actual())
                bl = map_nick2sd(name);
        }
        else
        {
            int num = conv_num(st, sdata);
            if (num >= 2000000)
                id = wrap<BlockId>(num);
            else if (num >= 150000)
            {
                dumb_ptr<map_session_data> p_sd = nullptr;
                if ((p_sd = map_nick2sd(map_charid2nick(wrap<CharId>(num)))) != nullptr)
                    id = p_sd->bl_id;
                else
                {
                    push_int<ScriptDataInt>(st->stack, -1);
                    return;
                }
            }
            else
            {
                push_int<ScriptDataInt>(st->stack, -1);
                return;
            }
            bl = map_id2bl(id);
        }

        if (bl == nullptr)
        {
            push_int<ScriptDataInt>(st->stack, -1);
            return;
        }
        int var = pc_readparam(bl, reg.sp());
        push_int<ScriptDataInt>(st->stack, var);
        return;
    }

    struct script_data *sdata = &AARG(1);
    get_val(st, sdata);

    SIR reg = AARG(0).get_if<ScriptDataVariable>()->reg;
    ZString name_ = variable_names.outtern(reg.base());
    char prefix = name_.front();
    char postfix = name_.back();

    if(prefix == '.')
    {
        if (name_.startswith(".@"_s))
        {
            PRINTF("builtin_get: illegal scope!\n"_fmt);
            push_int<ScriptDataInt>(st->stack, 0);
            return;
        }
        NpcName name;
        if (sdata->is<ScriptDataStr>())
        {
            name = stringish<NpcName>(ZString(conv_str(st, sdata)));
            bl = npc_name2id(name);
        }
        else
        {
            id = wrap<BlockId>(conv_num(st, sdata));
            bl = map_id2bl(id);
        }
    }
    else if(prefix != '$')
    {
        CharName name;
        if (sdata->is<ScriptDataStr>())
        {
            name = stringish<CharName>(ZString(conv_str(st, sdata)));
            if (name.to__actual())
                bl = map_nick2sd(name);
        }
        else
        {
            id = wrap<BlockId>(conv_num(st, sdata));
            bl = map_id2bl(id);
        }
    }
    else
    {
        PRINTF("builtin_get: illegal scope !\n"_fmt);
        push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    if (!bl)
    {
        if (postfix == '$')
            push_str<ScriptDataStr>(st->stack, ""_s);
        else
            push_int<ScriptDataInt>(st->stack, 0);
        return;
    }

    if (postfix == '$')
    {
        ZString var = pc_readregstr(bl, reg);
        push_str<ScriptDataStr>(st->stack, var);
    }
    else
    {
        int var = 0;
        if (prefix == '#' && bl)
        {
            if (name_.startswith("##"_s))
                var = pc_readaccountreg2(bl->is_player(), stringish<VarName>(name_));
            else
                var = pc_readaccountreg(bl->is_player(), stringish<VarName>(name_));
        }
        else if (bl)
            var = pc_readreg(bl, reg);

        push_int<ScriptDataInt>(st->stack, var);
    }
}

/*==========================================
 * Nude [Valaris]
 *------------------------------------------
 */
static
void builtin_nude(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    script_nullpo_end(sd, "player not found");

    for (EQUIP i : EQUIPs)
    {
        IOff0 idx = sd->equip_index_maybe[i];
        if (idx.ok())
            pc_unequipitem(sd, idx, CalcStatus::LATER);
    }
    pc_calcstatus(sd, 0);

}

/*==========================================
 * UnequipById [Freeyorp]
 *------------------------------------------
 */
static
void builtin_unequipbyid(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    EQUIP slot_id = EQUIP(conv_num(st, &AARG(0)));

    if (slot_id >= EQUIP() && slot_id < EQUIP::COUNT)
    {
        IOff0 idx = sd->equip_index_maybe[slot_id];
        if (idx.ok())
            pc_unequipitem(sd, idx, CalcStatus::LATER);
    }

    pc_calcstatus(sd, 0);

}

/*==========================================
 * npcwarp [remoitnane]
 * Move NPC to a new position on the same map.
 *------------------------------------------
 */
static
void builtin_npcwarp(ScriptState *st)
{
    int x, y;
    dumb_ptr<npc_data> nd = nullptr;

    x = conv_num(st, &AARG(0));
    y = conv_num(st, &AARG(1));
    NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(2))));
    nd = npc_name2id(npc);

    script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, npc));

    P<map_local> m = nd->bl_m;

    /* Crude sanity checks. */
    if (!nd->bl_prev
            || x < 0 || x > m->xs -1
            || y < 0 || y > m->ys - 1)
        return;

    npc_enable(npc, 0);
    map_delblock(nd); /* [Freeyorp] */
    nd->bl_x = x;
    nd->bl_y = y;
    map_addblock(nd);
    npc_enable(npc, 1);

}

/*==========================================
 * npcareawarp [remoitnane] [wushin]
 * Move NPC to a new area on the same map.
 *------------------------------------------
 */
static
void builtin_npcareawarp(ScriptState *st)
{
    int x0, y0, x1, y1, x, y, max, cb, lx = -1, ly = -1, j = 0;
    dumb_ptr<npc_data> nd = nullptr;

    NpcName npc = stringish<NpcName>(ZString(conv_str(st, &AARG(5))));
    nd = npc_name2id(npc);

    x0 = conv_num(st, &AARG(0));
    y0 = conv_num(st, &AARG(1));
    x1 = conv_num(st, &AARG(2));
    y1 = conv_num(st, &AARG(3));
    cb = conv_num(st, &AARG(4));

    if (!nd)
    {
        PRINTF("builtin_npcareawarp: no such npc: '%s'\n"_fmt, npc);
        return;
    }

    max = (y1 - y0 + 1) * (x1 - x0 + 1) * 3;
    if (max > 1000)
        max = 1000;

    P<map_local> m = nd->bl_m;
    if (cb) {
        do
        {
            x = random_::in(x0, x1);
            y = random_::in(y0, y1);
        }
        while (bool(map_getcell(m, x, y) & MapCell::UNWALKABLE)
             && (++j) < max);
        if (j >= max)
        {
            if (lx >= 0)
            {                   // Since reference went wrong, the place which boiled before is used.
                x = lx;
                y = ly;
            }
            else
                return;       // Since reference of the place which boils first went wrong, it stops.
        }
    }
    else
    {
        x = random_::in(x0, x1);
        y = random_::in(y0, y1);
    }

    npc_enable(npc, 0);
    map_delblock(nd); /* [Freeyorp] */
    nd->bl_x = x;
    nd->bl_y = y;
    map_addblock(nd);
    npc_enable(npc, 1);

}

/*==========================================
 * message [MouseJstr]
 *------------------------------------------
 */
static
void builtin_message(ScriptState *st)
{
    CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(0))));
    ZString msg = ZString(conv_str(st, &AARG(1)));

    dumb_ptr<map_session_data> pl_sd = map_nick2sd(player);
    if (pl_sd == nullptr)
        return;
    clif_displaymessage(pl_sd->sess, msg);

}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_title(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    ZString msg = ZString(conv_str(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    clif_npc_send_title(sd->sess, st->oid, msg);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_smsg(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    if (HARG(2))
    {
        CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(2))));
        sd = map_nick2sd(player);
    }

    int type = HARG(1) ? conv_num(st, &AARG(0)) : 0;
    ZString msg = ZString(conv_str(st, (HARG(1) ? &AARG(1) : &AARG(0))));
    script_nullpo_end(sd, "player not found");
    if (type < 0 || type > 0xFF)
        type = 0;

    clif_server_message(sd, type, msg);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_remotecmd(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    if (HARG(1))
    {
        CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(1))));
        sd = map_nick2sd(player);
    }

    ZString msg = ZString(conv_str(st, &AARG(0)));
    if (sd == nullptr)
        return;
    clif_remote_command(sd, msg);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_sendcollision(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    MapName map_name = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    int mask = conv_num(st, &AARG(1));
    short x1, y1, x2, y2;
    x1 = x2 = conv_num(st, &AARG(2));
    y1 = y2 = conv_num(st, &AARG(3));
    script_nullpo_end(sd, "player not found");

    if (HARG(5))
    {
        x2 = conv_num(st, &AARG(4));
        y2 = conv_num(st, &AARG(5));
        if (HARG(6))
        {
            CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(6))));
            sd = map_nick2sd(player);
        }
    }

    else if (HARG(4))
    {
        CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(4))));
        sd = map_nick2sd(player);
    }

    if (sd == nullptr)
        return;
    clif_update_collision(sd, x1, y1, x2, y2, map_name, mask);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_music(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    ZString msg = ZString(conv_str(st, &AARG(0)));
    script_nullpo_end(sd, "player not found");
    clif_change_music(sd, msg);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_mapmask(ScriptState *st)
{
    dumb_ptr<npc_data> nd;
    dumb_ptr<map_session_data> sd;
    int map_mask = conv_num(st, &AARG(0));

    if(st->oid)
        nd = map_id_is_npc(st->oid);
    if(st->rid)
        sd = script_rid2sd(st);

    if(HARG(1) && sd != nullptr)
        sd->bl_m->mask = map_mask;
    else if(HARG(1) && nd)
        nd->bl_m->mask = map_mask;

    script_nullpo_end(sd, "player not found");
    clif_send_mask(sd, map_mask);
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_getmask(ScriptState *st)
{
    dumb_ptr<npc_data> nd;
    dumb_ptr<map_session_data> sd;
    int map_mask;

    if(st->oid)
        nd = map_id_is_npc(st->oid);
    if(st->rid)
        sd = script_rid2sd(st);

    if(sd != nullptr)
        map_mask = sd->bl_m->mask;
    else if(nd)
        map_mask = nd->bl_m->mask;
    else
        map_mask = -1;

    push_int<ScriptDataInt>(st->stack, map_mask);
}

/*==========================================
 * npctalk (sends message to surrounding
 * area) [Valaris]
 *------------------------------------------
 */
static
void builtin_npctalk(ScriptState *st)
{
    dumb_ptr<npc_data> nd;
    RString str = conv_str(st, &AARG(1));

    nd = npc_name2id(stringish<NpcName>(ZString(conv_str(st, &AARG(0)))));
    if (nd == nullptr)
        return;


    if(HARG(2)){
        CharName player = stringish<CharName>(ZString(conv_str(st, &AARG(2))));
        dumb_ptr<map_session_data> pl_sd = map_nick2sd(player);
        if (pl_sd == nullptr)
            return;
        clif_message_towards(pl_sd, nd, str);
    }

    else
        clif_message(nd, str);
}

/*==========================================
 * register cmd
 *------------------------------------------
 */
static
void builtin_registercmd(ScriptState *st)
{
    RString evoke = conv_str(st, &AARG(0));
    ZString event_ = conv_str(st, &AARG(1));
    NpcEvent event;
    extract(event_, &event);
    spells_by_events.put(evoke, event);
}

/*==========================================
 * getlook char info. getlook(arg)
 *------------------------------------------
 */
static
void builtin_getlook(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    LOOK type = LOOK(conv_num(st, &AARG(0)));
    int val = -1;
    script_nullpo_end(sd, "player not found");

    switch (type)
    {
        case LOOK::HAIR:        //1
            val = sd->status.hair;
            break;
        case LOOK::WEAPON:      //2
            val = static_cast<uint16_t>(sd->status.weapon);
            break;
        case LOOK::HEAD_BOTTOM: //3
            val = unwrap<ItemNameId>(sd->status.head_bottom);
            break;
        case LOOK::HEAD_TOP:    //4
            val = unwrap<ItemNameId>(sd->status.head_top);
            break;
        case LOOK::HEAD_MID:    //5
            val = unwrap<ItemNameId>(sd->status.head_mid);
            break;
        case LOOK::HAIR_COLOR:  //6
            val = sd->status.hair_color;
            break;
        case LOOK::CLOTHES_COLOR:   //7
            val = sd->status.clothes_color;
            break;
        case LOOK::SHIELD:      //8
            val = unwrap<ItemNameId>(sd->status.shield);
            break;
        case LOOK::SHOES:       //9
            break;
    }

    push_int<ScriptDataInt>(st->stack, val);
}

/*==========================================
 * get char save point. argument: 0- map name, 1- x, 2- y
 *------------------------------------------
 */
static
void builtin_getsavepoint(ScriptState *st)
{
    int x, y, type;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    type = conv_num(st, &AARG(0));

    x = sd->status.save_point.x;
    y = sd->status.save_point.y;
    switch (type)
    {
        case 0:
        {
            RString mapname = sd->status.save_point.map_;
            push_str<ScriptDataStr>(st->stack, mapname);
        }
            break;
        case 1:
            push_int<ScriptDataInt>(st->stack, x);
            break;
        case 2:
            push_int<ScriptDataInt>(st->stack, y);
            break;
    }
}

/*==========================================
 * areatimer
 *------------------------------------------
 */
static
void builtin_areatimer_sub(dumb_ptr<block_list> bl, interval_t tick, NpcEvent event)
{
    if (!bl)
        return;
    if (bl->bl_type == BL::PC)
    {
        dumb_ptr<map_session_data> sd = map_id_is_player(bl->bl_id);
        pc_addeventtimer(sd, tick, event);
    }
}

/*==========================================
 * 
 *------------------------------------------
 */
static
void builtin_areatimer(ScriptState *st)
{
    int x0, y0, x1, y1, bl_num;

    bl_num = conv_num(st, &AARG(0));
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(1))));
    x0 = conv_num(st, &AARG(2));
    y0 = conv_num(st, &AARG(3));
    x1 = conv_num(st, &AARG(4));
    y1 = conv_num(st, &AARG(5));
    interval_t tick = static_cast<interval_t>(conv_num(st, &AARG(6)));
    ZString event_ = conv_str(st, &AARG(7));
    BL block_type;
    NpcEvent event;
    extract(event_, &event);

    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);

    switch (bl_num)
    {
        case 0:
            block_type = BL::PC;
            break;
        default:
            return;
    }

    map_foreachinarea(std::bind(builtin_areatimer_sub, ph::_1, tick, event),
            m,
            x0, y0,
            x1, y1,
            block_type);
}

/*==========================================
 * Check whether the PC is in the specified rectangle
 *------------------------------------------
 */
static
void builtin_isin(ScriptState *st)
{
    int x1, y1, x2, y2;
    dumb_ptr<map_session_data> sd = script_rid2sd(st);

    MapName str = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    x1 = conv_num(st, &AARG(1));
    y1 = conv_num(st, &AARG(2));
    x2 = conv_num(st, &AARG(3));
    y2 = conv_num(st, &AARG(4));

    script_nullpo_end(sd, "player not found");

    push_int<ScriptDataInt>(st->stack,
              (sd->bl_x >= x1 && sd->bl_x <= x2)
              && (sd->bl_y >= y1 && sd->bl_y <= y2)
              && (str == sd->bl_m->name_));
}

/*==========================================
 * Check whether the coords are collision
 *------------------------------------------
 */
static
void builtin_iscollision(ScriptState *st)
{
    int x, y;
    MapName mapname = stringish<MapName>(ZString(conv_str(st, &AARG(0))));
    P<map_local> m = TRY_UNWRAP(map_mapname2mapid(mapname), return);

    x = conv_num(st, &AARG(1));
    y = conv_num(st, &AARG(2));

    push_int<ScriptDataInt>(st->stack,
        bool(map_getcell(m, x, y) & MapCell::UNWALKABLE));
}

/*==========================================
 * Trigger the shop on a (hopefully) nearby shop NPC
 *------------------------------------------
 */
static
void builtin_shop(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    dumb_ptr<npc_data> nd;

    script_nullpo_end(sd, "player not found");

    NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
    nd = npc_name2id(name);
    script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, name));

    builtin_close(st);
    clif_npcbuysell(sd, nd->bl_id);
}

/*==========================================
 * Check whether the PC is dead
 *------------------------------------------
 */
static
void builtin_isdead(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");

    push_int<ScriptDataInt>(st->stack, pc_isdead(sd));
}

/*========================================
 * Changes a NPC name, and sprite
 *----------------------------------------
 */
static
void builtin_fakenpcname(ScriptState *st)
{
    NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
    NpcName newname = stringish<NpcName>(ZString(conv_str(st, &AARG(1))));
    Species newsprite = wrap<Species>(static_cast<uint16_t>(conv_num(st, &AARG(2))));
    dumb_ptr<npc_data> nd = npc_name2id(name);
    script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, name));
    nd->name = newname;
    nd->npc_class = newsprite;

    // Refresh this npc
    npc_enable(name, 0);
    npc_enable(name, 1);

}

/*============================
 * Gets the PC's x pos
 *----------------------------
 */
static
void builtin_getx(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, sd->bl_x);
}

/*============================
 * Gets the PC's y pos
 *----------------------------
 */
static
void builtin_gety(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, sd->bl_y);
}

/*============================
 * Gets the PC's direction
 *----------------------------
 */
static
void builtin_getdir(ScriptState *st)
{
    dumb_ptr<map_session_data> sd = script_rid2sd(st);
    script_nullpo_end(sd, "player not found");
    push_int<ScriptDataInt>(st->stack, static_cast<uint8_t>(sd->dir));
}

/*==========================================
 * Get the PC's current map's name
 *------------------------------------------
 */
static
void builtin_getmap(ScriptState *st)
{
    dumb_ptr<map_session_data> sd;

    if (HARG(0))    //指定したキャラを状態異常にする | Make the specified character abnormal
        sd = map_id_is_player(wrap<BlockId>(conv_num(st, &AARG(0))));
    else
        sd = script_rid2sd(st);

    if (!sd || !as_raw_pointer(Some(sd->bl_m)) || sd->bl_m == borrow(undefined_gat)) {
        push_str<ScriptDataStr>(st->stack, ""_s);
        return;
    }

    push_str<ScriptDataStr>(st->stack, sd->bl_m->name_);
}

/*==========================================
 * Get the NPC's info
 *------------------------------------------
 */
static
void builtin_strnpcinfo(ScriptState *st)
{
    int num = conv_num(st, &AARG(0));
    RString name;
    dumb_ptr<npc_data> nd;

    if(HARG(1)){
        struct script_data *sdata = &AARG(1);
        get_val(st, sdata);

        if (sdata->is<ScriptDataStr>())
        {
            NpcName name_ = stringish<NpcName>(ZString(conv_str(st, sdata)));
            nd = npc_name2id(name_);
        }
        else
        {
            BlockId id = wrap<BlockId>(conv_num(st, sdata));
            nd = map_id_is_npc(id);
        }
    } else {
        nd = map_id_is_npc(st->oid);
    }

    script_nullpo_end(nd, "npc not found");

    switch(num)
    {
        case 0:
            name = nd->name;
            break;
        case 1:
            name = nd->name.xislice_h(std::find(nd->name.begin(), nd->name.end(), '#'));
            break;
        case 2:
            name = nd->name.xislice_t(std::find(nd->name.begin(), nd->name.end(), '#'));
            break;
        case 3:
            name = nd->bl_m->name_;
            break;
    }

    push_str<ScriptDataStr>(st->stack, name);
}

/*============================
 * Gets the NPC's x pos
 *----------------------------
 */
static
void builtin_getnpcx(ScriptState *st)
{
    dumb_ptr<npc_data> nd;

    if(HARG(0)){
        NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
        nd = npc_name2id(name);
        script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, name));
    } else {
        nd = map_id_is_npc(st->oid);
    }

    script_nullpo_end(nd, "no npc");

    push_int<ScriptDataInt>(st->stack, nd->bl_x);
}

/*============================
 * Gets the NPC's y pos
 *----------------------------
 */
static
void builtin_getnpcy(ScriptState *st)
{
    dumb_ptr<npc_data> nd;

    if(HARG(0)){
        NpcName name = stringish<NpcName>(ZString(conv_str(st, &AARG(0))));
        nd = npc_name2id(name);
        script_nullpo_end(nd, STRPRINTF("no such npc: '%s'"_fmt, name));
    } else {
        nd = map_id_is_npc(st->oid);
    }

    script_nullpo_end(nd, "no npc");

    push_int<ScriptDataInt>(st->stack, nd->bl_y);
}

/*==========================================
 *
 *------------------------------------------
 */
static
void builtin_mapexit(ScriptState *)
{
    runflag = 0;
}


#define BUILTIN(func, args, ret)    \
{builtin_##func, #func ## _s, args, ret}

BuiltinFunction builtin_functions[] =
{
    BUILTIN(mes, "?"_s, '\0'),
    BUILTIN(clear, ""_s, '\0'),
    BUILTIN(goto, "L"_s, '\0'),
    BUILTIN(callfunc, "F"_s, '\0'),
    BUILTIN(call, "F?*"_s, '.'),
    BUILTIN(callsub, "L"_s, '\0'),
    BUILTIN(getarg, "i?"_s, 'v'),
    BUILTIN(return, "?"_s, '\0'),
    BUILTIN(void, "?*"_s, '\0'),
    BUILTIN(next, ""_s, '\0'),
    BUILTIN(close, ""_s, '\0'),
    BUILTIN(close2, ""_s, '\0'),
    BUILTIN(menu, "sL**"_s, '\0'),
    BUILTIN(rand, "i?"_s, 'i'),
    BUILTIN(isat, "Mxy"_s, 'i'),
    BUILTIN(warp, "Mxy"_s, '\0'),
    BUILTIN(areawarp, "MxyxyMxy"_s, '\0'),
    BUILTIN(heal, "ii?"_s, '\0'),
    BUILTIN(injure, "iii"_s, '\0'),
    BUILTIN(input, "N"_s, '\0'),
    BUILTIN(requestitem, "N?"_s, '\0'),
    BUILTIN(requestlang, "N"_s, '\0'),
    BUILTIN(if, "iF*"_s, '\0'),
    BUILTIN(elif, "iF*"_s, '\0'),
    BUILTIN(else, "F*"_s, '\0'),
    BUILTIN(set, "Ne?"_s, '\0'),
    BUILTIN(get, "Ne"_s, 'v'),
    BUILTIN(setarray, "Ne*"_s, '\0'),
    BUILTIN(cleararray, "Nei"_s, '\0'),
    BUILTIN(getarraysize, "N"_s, 'i'),
    BUILTIN(getelementofarray, "Ni"_s, 'v'),
    BUILTIN(array_search, "eN"_s, 'i'),
    BUILTIN(setlook, "ii"_s, '\0'),
    BUILTIN(countitem, "I"_s, 'i'),
    BUILTIN(checkweight, "Ii"_s, 'i'),
    BUILTIN(getitem, "Ii??"_s, '\0'),
    BUILTIN(makeitem, "IiMxy"_s, '\0'),
    BUILTIN(delitem, "Ii"_s, '\0'),
    BUILTIN(getcharid, "i?"_s, 'i'),
    BUILTIN(getnpcid, "?"_s, 'i'),
    BUILTIN(getversion, ""_s, 'i'),
    BUILTIN(strcharinfo, "i?"_s, 's'),
    BUILTIN(getequipid, "i?"_s, 'i'),
    BUILTIN(bonus, "ii"_s, '\0'),
    BUILTIN(bonus2, "iii"_s, '\0'),
    BUILTIN(skill, "ii?"_s, '\0'),
    BUILTIN(setskill, "ii"_s, '\0'),
    BUILTIN(getskilllv, "i"_s, 'i'),
    BUILTIN(overrideattack, "??????"_s, '\0'),
    BUILTIN(getgmlevel, ""_s, 'i'),
    BUILTIN(end, ""_s, '\0'),
    BUILTIN(getopt2, ""_s, 'i'),
    BUILTIN(setopt2, "i"_s, '\0'),
    BUILTIN(savepoint, "Mxy"_s, '\0'),
    BUILTIN(gettimetick, "i"_s, 'i'),
    BUILTIN(gettime, "i"_s, 'i'),
    BUILTIN(openstorage, ""_s, '\0'),
    BUILTIN(getexp, "ii"_s, '\0'),
    BUILTIN(mobinfo, "ii"_s, 'v'),
    BUILTIN(mobinfo_droparrays, "iiN"_s, 'i'),
    BUILTIN(getmobdrops, "i"_s, 'i'),
    BUILTIN(summon, "Mxyssmii?"_s, '\0'),
    BUILTIN(monster, "Mxysmi?"_s, '\0'),
    BUILTIN(areamonster, "Mxyxysmi?"_s, '\0'),
    BUILTIN(killmonster, "ME"_s, '\0'),
    BUILTIN(donpcevent, "E"_s, '\0'),
    BUILTIN(addtimer, "tE?"_s, '\0'),
    BUILTIN(addnpctimer, "tE"_s, '\0'),
    BUILTIN(initnpctimer, "?"_s, '\0'),
    BUILTIN(startnpctimer, "?"_s, '\0'),
    BUILTIN(stopnpctimer, "?"_s, '\0'),
    BUILTIN(getnpctimer, "i?"_s, 'i'),
    BUILTIN(setnpctimer, "i?"_s, '\0'),
    BUILTIN(setnpcdirection, "iii?"_s, '\0'),
    BUILTIN(npcaction, "i???"_s, '\0'),
    BUILTIN(camera, "???"_s, '\0'),
    BUILTIN(announce, "si"_s, '\0'),
    BUILTIN(mapannounce, "Msi"_s, '\0'),
    BUILTIN(getusers, "i"_s, 'i'),
    BUILTIN(getmapusers, "M"_s, 'i'),
    BUILTIN(getareausers, "Mxyxy?"_s, 'i'),
    BUILTIN(getareadropitem, "Mxyxyi?"_s, 'i'),
    BUILTIN(enablenpc, "s"_s, '\0'),
    BUILTIN(disablenpc, "s"_s, '\0'),
    BUILTIN(sc_start, "iTi?"_s, '\0'),
    BUILTIN(sc_end, "i?"_s, '\0'),
    BUILTIN(sc_check, "i?"_s, 'i'),
    BUILTIN(debugmes, "s"_s, '\0'),
    BUILTIN(wgm, "s"_s, '\0'),
    BUILTIN(gmlog, "s"_s, '\0'),
    BUILTIN(resetstatus, ""_s, '\0'),
    BUILTIN(attachrid, "i"_s, 'i'),
    BUILTIN(detachrid, ""_s, '\0'),
    BUILTIN(isloggedin, "i"_s, 'i'),
    BUILTIN(setmapflag, "Mi"_s, '\0'),
    BUILTIN(removemapflag, "Mi"_s, '\0'),
    BUILTIN(getmapflag, "Mi"_s, 'i'),
    BUILTIN(pvpon, "M"_s, '\0'),
    BUILTIN(pvpoff, "M"_s, '\0'),
    BUILTIN(setpvpchannel, "i"_s, '\0'),
    BUILTIN(getpvpflag, "i?"_s, 'i'),
    BUILTIN(emotion, "i?"_s, '\0'),
    BUILTIN(mapwarp, "MMxy"_s, '\0'),
    BUILTIN(mobcount, "ME"_s, 'i'),
    BUILTIN(marriage, "P"_s, 'i'),
    BUILTIN(divorce, ""_s, 'i'),
    BUILTIN(getitemlink, "I"_s, 's'),
    BUILTIN(getpartnerid2, ""_s, 'i'),
    BUILTIN(explode, "Nss"_s, '\0'),
    BUILTIN(getinventorylist, ""_s, '\0'),
    BUILTIN(getactivatedpoolskilllist, ""_s, '\0'),
    BUILTIN(getunactivatedpoolskilllist, ""_s, '\0'),
    BUILTIN(poolskill, "i"_s, '\0'),
    BUILTIN(unpoolskill, "i"_s, '\0'),
    BUILTIN(misceffect, "i?"_s, '\0'),
    BUILTIN(specialeffect, "i"_s, '\0'),
    BUILTIN(specialeffect2, "i"_s, '\0'),
    BUILTIN(nude, ""_s, '\0'),
    BUILTIN(unequipbyid, "i"_s, '\0'),
    BUILTIN(npcwarp, "xys"_s, '\0'),
    BUILTIN(npcareawarp, "xyxyis"_s, '\0'),
    BUILTIN(message, "Ps"_s, '\0'),
    BUILTIN(npctalk, "ss?"_s, '\0'),
    BUILTIN(registercmd, "ss"_s, '\0'),
    BUILTIN(title, "s"_s, '\0'),
    BUILTIN(smsg, "e??"_s, '\0'),
    BUILTIN(remotecmd, "s?"_s, '\0'),
    BUILTIN(sendcollision, "Mixy???"_s, '\0'),
    BUILTIN(music, "s"_s, '\0'),
    BUILTIN(mapmask, "i?"_s, '\0'),
    BUILTIN(getmask, ""_s, 'i'),
    BUILTIN(getlook, "i"_s, 'i'),
    BUILTIN(getsavepoint, "i"_s, 'v'),
    BUILTIN(areatimer, "MxyxytEi"_s, '\0'),
    BUILTIN(foreach, "iMxyxyE?"_s, '\0'),
    BUILTIN(isin, "Mxyxy"_s, 'i'),
    BUILTIN(iscollision, "Mxy"_s, 'i'),
    BUILTIN(shop, "s"_s, '\0'),
    BUILTIN(isdead, ""_s, 'i'),
    BUILTIN(aggravate, "i?"_s, '\0'),
    BUILTIN(fakenpcname, "ssi"_s, '\0'),
    BUILTIN(puppet, "mxysi??"_s, 'i'),
    BUILTIN(destroy, "?"_s, '\0'),
    BUILTIN(getx, ""_s, 'i'),
    BUILTIN(gety, ""_s, 'i'),
    BUILTIN(getdir, ""_s, 'i'),
    BUILTIN(getnpcx, "?"_s, 'i'),
    BUILTIN(getnpcy, "?"_s, 'i'),
    BUILTIN(strnpcinfo, "i?"_s, 's'),
    BUILTIN(getmap, "?"_s, 's'),
    BUILTIN(mapexit, ""_s, '\0'),
    BUILTIN(freeloop, "i"_s, '\0'),
    BUILTIN(if_then_else, "iii"_s, 'v'),
    BUILTIN(max, "e?*"_s, 'i'),
    BUILTIN(min, "e?*"_s, 'i'),
    BUILTIN(average, "ii*"_s, 'i'),
    BUILTIN(sqrt, "i"_s, 'i'),
    BUILTIN(cbrt, "i"_s, 'i'),
    BUILTIN(pow, "ii"_s, 'i'),
    BUILTIN(target, "iii"_s, 'i'),
    BUILTIN(distance, "ii?"_s, 'i'),
    BUILTIN(chr, "i"_s, 's'),
    BUILTIN(ord, "s"_s, 'i'),
    {nullptr, ""_s, ""_s, '\0'},
};
} // namespace map
} // namespace tmwa