Sign in to follow this  
Followers 0
Phelix

Python Script - Auto generate models for Mapping

10 posts in this topic

xphoto-204.png,q_r=1422818183.pagespeed.

 

Python, Auto-Generator for mapping

 

The Auto-Generator currently allows Mappers to generate multiple models to their current map in a allocated area of co-ordinates (x and why).

 

Features:

- Click-able buttons.
- Contains Text-fields for input of numbers.
- Auto finds directory for call of duty 4 for cd and steam, any other location will be prompted with another window to locate.
- Contains list menus with scrollbars with all the models / maps.
- If you type a wrong input the program won't allow you to proceed onto putting quantities.
- If you accidentally select a model and update and find out you don't want that model, you can deselect and update.
- Application format (Minimise, Maximise, close etc).
- Better GUI - Tried to make it as user-friendly as possible.

 

 

Here's a preview of what the program looks like:

 

 

10120d3160f54b989957b88143985d28.png

 

- Phelix

 

 

Instructions
1. Download the .rar file which can be downloaded here: https://www.dropbox.com/s/zsewi3vclgomdun/Auto-Generator.rar?dl=0

2. Extract the contents within the rar file to a place you can access

3. Open the executable file within the extracted contents called: Auto-Generator.exe
4. Follow the instructions within the program

 

To enable the Z co-ordinate, click the enable z button then input z value (by default if z isn't enabled, z is = to 16)

Always press the update button when you change co-ordinates, models or map.

 

Note: As far as map requirements go, you just need the map file which the program is made so you can only select map files that exist.

MAKE SURE YOU BACKUP MAP BEFORE ATTEMPTING JUST IN CASE

For those who are wondering the program was done in 'PyCharm 4.0.4' and the interface was purely done through code. I Hope you enjoy and as mentioned before, any problems, be sure to mention them at my Xfire/steam or below!

Program was fully created by me with pure Python, for the GUI i used the Tkinter module.

Update: 
- I will make a video showing what to do soon, thus meaning people shouldn't get confused on how to use the program. 
- Working on a undo option so therefore when you check and it's not to your standards, you can undo and restart.

It would be helpful if you post any suggestions you wish for me to add (doesn't have to be about the generator, can be turned into a tool based utility program for Call of Duty4). I also know my threads are long so sorry for that and if you did however read through it all, i appreciate your time! smile.png -Phelix.

ANY BUGS, PLEASE REPORT HERE OR THROUGH MY CONTACT DETAILS BELOW!

 

Source code

from tkinter import *
from tkinter.filedialog import askdirectory
from tkinter import ttk
from PIL import Image, ImageTk
import os
from random import randrange


class MainWindow(Frame):
    def __init__(self, master=None):
        Frame.__init__(self, master)
        self.default_directory_cd = os.path.isdir('C:\Program Files\Activision\Call of Duty 4 - Modern Warfare')
        self.default_directory_steam = os.path.isdir('C:\Program Files (x86)\Steam\steamapps\common\Call of Duty 4')
        self.master = master
        self.z_check = IntVar()
        self.x_value = StringVar()
        self.x_value2 = StringVar()
        self.y_value = StringVar()
        self.y_value2 = StringVar()
        self.z_value = StringVar()
        self.z_value2 = StringVar()
        self.x = StringVar()
        self.x2 = StringVar()
        self.y = StringVar()
        self.y2 = StringVar()
        self.z = StringVar()
        self.model = StringVar()
        self.map = StringVar()
        self.z_value_entry_active = False
        self.map_chosen = StringVar()
        self.models_chosen = []
        self.models_entry_box = []
        self.directory_chosen = bool
        self.models_entry_box_created = bool
        self.model_entry_dict = {}
        self.model_entry_dict2 = []
        self.model_entry_dict_text = {}
        self.old_models = []
        self.x_is_valid = bool
        self.y_is_valid = bool
        self.z_is_valid = bool
        self.map_listbox_has_entries = bool
        self.errors_check_label_bool = False
        self.apply_preview_undo = False
        self.extend = 620
        self.var = StringVar()

        self.init_window()

    def init_window(self):
        self.master.title('Auto-Generator')
        self.pack(fill=BOTH, expand=1)

        auto_image = Image.open('auto.png')
        render_image = ImageTk.PhotoImage(auto_image)
        image_print = Label(self, image=render_image)
        image_print.image = render_image
        image_print.pack()

        author = ttk.LabelFrame(self)
        author.pack(fill=X)
        author_text = ttk.Label(author, text='--- Program by Phelix. --- Auto-generator V1.1 ---')
        author_text.pack()

        x_value_frame = ttk.LabelFrame(self, text='Please input the X values', width=40)
        x_value_frame.pack(fill=X)
        x_value_entry_label = ttk.Label(x_value_frame, text='From:')
        x_value_entry_label.pack(fill=X, side=LEFT)
        self.x_value_entry = ttk.Entry(x_value_frame,  width=10, textvariable=self.x_value)
        self.x_value_entry.pack(side=LEFT)
        x_value_entry_label2 = ttk.Label(x_value_frame, text='To:')
        x_value_entry_label2.pack(fill=X, side=LEFT)
        self.x_value_entry2 = ttk.Entry(x_value_frame,  width=10, textvariable=self.x_value2)
        self.x_value_entry2.pack(side=LEFT)

        y_value_frame = ttk.LabelFrame(self, text='Please input the Y values', width=40)
        y_value_frame.pack(fill=X)
        y_value_entry_label = ttk.Label(y_value_frame, text='From:')
        y_value_entry_label.pack(fill=X, side=LEFT)
        self.y_value_entry = ttk.Entry(y_value_frame, width=10, textvariable=self.y_value)
        self.y_value_entry.pack(side=LEFT)
        y_value_entry_label2 = ttk.Label(y_value_frame, text='To:')
        y_value_entry_label2.pack(fill=X, side=LEFT)
        self.y_value_entry2 = ttk.Entry(y_value_frame, width=10, textvariable=self.y_value2)
        self.y_value_entry2.pack(side=LEFT)

        z_value_frame = ttk.LabelFrame(self, text='Please input the Z value', width=40)
        z_value_frame.pack(fill=X)
        self.z_value_entry = ttk.Entry(z_value_frame, width=10, state=DISABLED, textvariable=self.z_value)
        self.z_value_entry.pack(side=LEFT)
        self.z_value_enable = ttk.Button(z_value_frame, text='Enable Z', command=lambda: self.enable_z())
        self.z_value_enable.pack(side=RIGHT)

        self.select_map_directory = ttk.Button(self, text='1. Select Map Directory / Automatically finds if default',
                                          command=lambda: self.map_directory())
        self.select_map_directory.pack(fill=X)

        self.model_and_map_frame = ttk.LabelFrame(self, text='Map & Model List boxes')

        self.model_scrollbar = Scrollbar(self.model_and_map_frame)
        self.map_scrollbar = Scrollbar(self.model_and_map_frame)

        self.map_listbox = Listbox(self.model_and_map_frame, height=10, width=30, selectmode=SINGLE, exportselection=0)
        self.map_listbox.pack(side=LEFT, fill=Y)

        self.map_scrollbar.pack(side=LEFT, fill=Y)
        self.map_scrollbar.config(command=self.map_listbox.yview)

        self.model_listbox = Listbox(self.model_and_map_frame, height=10, width=30, selectmode=MULTIPLE, exportselection=0,
                                     yscrollcommand=self.model_scrollbar.set)
        self.model_listbox.pack(side=LEFT, fill=Y)

        self.model_scrollbar.pack(side=LEFT, fill=Y)
        self.model_scrollbar.config(command=self.model_listbox.yview)

        self.model_and_map_frame.pack(side=LEFT, fill=Y)

        proceed_frame = ttk.LabelFrame(self, text='Checkbox')
        proceed_frame.pack(fill=X)

        x_value_check = ttk.Label(proceed_frame, text='X value:')
        x_value_check.pack(fill=X)

        y_value_check = ttk.Label(proceed_frame, text='Y value:')
        y_value_check.pack(fill=X)

        z_value_check = ttk.Label(proceed_frame, text='Z value:')
        z_value_check.pack(fill=X)

        map_chosen = ttk.Label(proceed_frame, text='Map Chosen:')
        map_chosen.pack(fill=X)

        self.map_chosen_listbox = Listbox(proceed_frame, height=1, width=30)
        self.map_chosen_listbox.pack(fill=X)

        self.map_chosen_listbox.insert(1, "Map not chosen yet!")
        self.map_chosen_listbox['state'] = DISABLED

        model_chosen = ttk.Label(proceed_frame, text='Model(s) Chosen:')
        model_chosen.pack(fill=X)

        self.model_chosen_listbox = Listbox(proceed_frame, height=5, width=30)
        self.model_chosen_listbox.pack(fill=X)

        self.model_chosen_listbox.insert(1, "Model(s) not chosen yet!")
        self.model_chosen_listbox['state'] = DISABLED

        self.entry_frame = ttk.LabelFrame(self, text='Entries')
        self.entry_frame.pack(fill=X)

        self.check_button = ttk.Button(proceed_frame, text='Update button', command=lambda: self.check_values())
        self.check_button.pack(fill=X)

    def enable_z(self):
        self.z_value_entry['state'] = ACTIVE
        self.z_value_entry_active = True
        self.z_value_enable['command'] = lambda: self.disable_z()
        self.z_value_enable['text'] = 'Disable Z'

    def disable_z(self):
        self.z_value_entry['state'] = DISABLED
        self.z_value_entry_active = False
        self.z_value_enable['command'] = lambda: self.enable_z()
        self.z_value_enable['text'] = 'Enable Z'

    def map_directory(self):
        self.default_directory_cd_true = False
        self.default_directory_steam_true = False
        self.default_directory_cd_steam_true = False
        if self.default_directory_cd and not self.default_directory_steam:
            self.primary_directory1 = 'C:\Program Files\Activision\Call of Duty 4 - Modern Warfare\map_source'
            file_names = os.listdir(self.primary_directory1)
            list_length = len(file_names)
            i = 0
            self.map_listbox.delete(0, END)
            while i <= list_length - 1:
                self.map_listbox.insert(i, file_names[i])
                i += 1
            self.default_directory_cd_true = True
        elif self.default_directory_steam and not self.default_directory_cd:
            self.primary_directory2 = 'C:\Program Files (x86)\Steam\steamapps\common\Call of Duty 4\map_source'
            file_names = os.listdir(self.primary_directory2)
            list_length = len(file_names)
            i = 0
            self.map_listbox.delete(0, END)
            while i <= list_length - 1:
                self.map_listbox.insert(i, file_names[i])
                i += 1
            self.default_directory_steam_true = True
        elif self.default_directory_steam and self.default_directory_cd:
            self.primary_directory2 = 'C:\Program Files (x86)\Steam\steamapps\common\Call of Duty 4\map_source'
            file_names = os.listdir(self.primary_directory2)
            list_length = len(file_names)
            i = 0
            self.map_listbox.delete(0, END)
            while i <= list_length - 1:
                self.map_listbox.insert(i, file_names[i])
                i += 1
            self.default_directory_cd_steam_true = True
        else:
            self.primary_directory3 = askdirectory()
            file_names = os.listdir(self.primary_directory3)
            list_length = len(file_names)
            i = 0
            self.map_listbox.delete(0, END)
            while i <= list_length - 1:
                self.map_listbox.insert(i, file_names[i])
                i += 1
        self.model_directory()

    def model_directory(self):
        if self.default_directory_cd and not self.default_directory_steam:
            primary_directory = 'C:\\Program Files\\Activision\\Call of Duty 4 - Modern Warfare\\raw\\xmodel'
            file_names = os.listdir(primary_directory)
            list_length = len(file_names)
            i = 0
            self.model_listbox.delete(0, END)
            while i <= list_length - 1:
                self.model_listbox.insert(i, file_names[i])
                i += 1
        elif self.default_directory_steam and not self.default_directory_cd:
            primary_directory = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Call of Duty 4\\raw\\xmodel'
            file_names = os.listdir(primary_directory)
            list_length = len(file_names)
            i = 0
            self.model_listbox.delete(0, END)
            while i <= list_length - 1:
                self.model_listbox.insert(i, file_names[i])
                i += 1
        elif self.default_directory_steam and self.default_directory_cd:
            primary_directory = 'C:\\Program Files (x86)\\Steam\\steamapps\\common\\Call of Duty 4\\raw\\xmodel'
            file_names = os.listdir(primary_directory)
            list_length = len(file_names)
            i = 0
            self.model_listbox.delete(0, END)
            while i <= list_length - 1:
                self.model_listbox.insert(i, file_names[i])
                i += 1
        else:
            primary_directory = askdirectory()
            file_names = os.listdir(primary_directory)
            list_length = len(file_names)
            i = 0
            self.model_listbox.delete(0, END)
            while i <= list_length - 1:
                self.model_listbox.insert(i, file_names[i])
                i += 1

    def check_values(self):
        not_a_integer = "This is not an integer!                         "
        blank_field = "Empty field! Please input a number!                         "
        not_active = "Z is not enabled!                                                    "
        self.x_is_valid = False
        self.y_is_valid = False
        self.z_is_valid = False
        x_error = ttk.Label(self, text="", foreground="red")
        x_error.place(x=452, y=318)

        y_error = ttk.Label(self, text="", foreground="red")
        y_error.place(x=452, y=337)

        z_error = ttk.Label(self, text="", foreground="red")
        z_error.place(x=452, y=356)
        if self.x_value and self.x_value2:
            self.x = self.x_value.get()
            self.x2 = self.x_value2.get()
            if any(c.isalpha() for c in self.x) or any(c.isalpha() for c in self.x2):
                x_error['text'] = not_a_integer
                self.x_is_valid = False
            elif not self.x or not self.x2:
                x_error['text'] = blank_field
                self.x_is_valid = False
            elif self.x == self.x2:
                x_error['text'] = "Values are the same                                                       "
                self.x_is_valid = False
            else:
                x_error['foreground'] = "green"
                x_error['text'] = "%s / %s                                                            " % (self.x, self.x2)
                self.x_is_valid = True
        if self.y_value and self.y_value2:
            self.y = self.y_value.get()
            self.y2 = self.y_value2.get()
            if any(c.isalpha() for c in self.y) or any(c.isalpha() for c in self.y2):
                y_error['text'] = not_a_integer
                self.y_is_valid = False
            elif not self.y or not self.y2:
                y_error['text'] = blank_field
                self.y_is_valid = False
            elif self.y == self.y2:
                y_error['text'] = "Values are the same                                                       "
                self.y_is_valid = False

            else:
                y_error['foreground'] = "green"
                y_error['text'] = "%s / %s                                                              " % (self.y, self.y2)
                self.y_is_valid = True
        if self.z_value:
            if self.z_value_entry_active == True:
                self.z = self.z_value.get()
                if any(c.isalpha() for c in self.z):
                    z_error['text'] = not_a_integer
                    self.z_is_valid = False
                elif not self.z:
                    z_error['text'] = blank_field
                    self.z_is_valid = False
                else:
                    z_error['foreground'] = "green"
                    z_error['text'] = "%s                                                              " % self.z
                    self.z_is_valid = True
            else:
                z_error['foreground'] = "green"
                z_error['text'] = not_active
        self.check_map()
        self.check_models()

    def check_map(self):
        if not self.map_listbox.curselection():
            self.map_chosen_listbox['state'] = NORMAL
            self.map_chosen_listbox.delete(0)
            self.map_chosen_listbox.insert(0, "Map not chosen yet!")
            self.map_chosen_listbox['state'] = DISABLED
        else:
            self.map_chosen = self.map_listbox.get(self.map_listbox.curselection()[0])
            self.map_chosen_listbox['state'] = NORMAL
            self.map_chosen_listbox.delete(0)
            self.map_chosen_listbox.insert(0, str(self.map_chosen))
            self.map_chosen_listbox['state'] = DISABLED
            if self.default_directory_cd_true:
                self.map_file_location = 'C:\Program Files\Activision\Call of Duty 4 - Modern Warfare\map_source\%s' % self.map_chosen
            elif self.default_directory_steam_true:
                self.map_file_location = 'C:\Program Files (x86)\Steam\steamapps\common\Call of Duty 4\map_source\%s' % self.map_chosen
            elif self.default_directory_cd_steam_true:
                self.map_file_location = 'C:\Program Files (x86)\Steam\steamapps\common\Call of Duty 4\map_source\%s' % self.map_chosen

    def check_models(self):
        selection = len(self.model_listbox.curselection())
        self.model_chosen_listbox['state'] = NORMAL
        self.model_chosen_listbox.delete(0, END)
        self.models_chosen = []
        i = 0
        while i <= selection - 1 and selection != 0:
            self.models_chosen.append(self.model_listbox.get(self.model_listbox.curselection()[i]))
            i += 1
        if selection == 0:
            self.model_chosen_listbox['state'] = NORMAL
            self.model_chosen_listbox.insert(1, "Model(s) not chosen yet!")
            self.model_chosen_listbox['state'] = DISABLED
        self.insert(self.model_chosen_listbox, self.models_chosen, selection)
        self.generate_entries()

    def insert(self, listbox, item, amount):
        self.amount = amount
        self.listbox = listbox
        self.item = item

        i = 0
        while i <= amount - 1:
            self.listbox['state'] = NORMAL
            self.listbox.insert(i, str(self.item[i]))
            self.listbox['state'] = DISABLED
            i += 1

    def generate_entries(self):
        self.vars = []
        for i in range(len(self.models_chosen)):
            self.var = StringVar()
            self.vars.append(self.var)
        if self.x_is_valid and self.y_is_valid and self.map_chosen and self.models_chosen and not self.z_value_entry_active \
                or self.x_is_valid and self.y_is_valid and self.map_chosen and self.models_chosen and self.z_is_valid:
            if self.errors_check_label_bool:
                self.errors_check_label.destroy()
            if self.apply_preview_undo:
                self.apply_button.destroy()
                self.exit_button.destroy()
            if self.models_entry_box_created:
                for i in range(len(self.old_models)):
                    self.model_entry_dict_text["entry" + str(i)].destroy()
                    self.model_entry_dict["entry" + str(i)].destroy()
            for i in range(len(self.models_chosen)):
                self.model_entry_dict_text["entry" + str(i)] = ttk.Label(
                    self.entry_frame,
                    text="Quantity of: " + self.models_chosen[i]
                )
                self.model_entry_dict_text["entry" + str(i)].pack(fill=X)
                self.model_entry_dict["entry" + str(i)] = Spinbox(
                    self.entry_frame,
                    from_=0,
                    to=10,
                    textvariable=self.vars[i]
                )
                self.model_entry_dict["entry" + str(i)].pack(fill=X)
                self.models_entry_box_created = True
                self.old_models = self.models_chosen
            if len(self.models_chosen) > 0:
                self.extend = 660
                for i in range(len(self.models_chosen)):
                    self.extend += 40
                    root.geometry("642x%s" % self.extend)
            if len(self.models_chosen) < 1:
                root.geometry("642x620")
            self.apply_button = ttk.Button(self.entry_frame, text='Apply', command=lambda: self.apply())
            self.apply_button.pack(fill=X)

            self.exit_button = ttk.Button(self.entry_frame, text='Exit', state=DISABLED, command=lambda: root.quit())
            self.exit_button.pack(fill=X)
            self.apply_preview_undo = True

        else:
            if self.models_entry_box_created:
                for i in range(len(self.old_models)):
                    self.model_entry_dict_text["entry" + str(i)].destroy()
                    self.model_entry_dict["entry" + str(i)].destroy()
            if self.errors_check_label_bool:
                self.errors_check_label.destroy()
                self.errors_check_label_bool = False
            if self.apply_preview_undo:
                self.apply_button.destroy()
                self.exit_button.destroy()
                self.apply_preview_undo = False

            self.errors_check_label = ttk.Label(self.entry_frame, text='Check for error(s)! (checkbox)')
            self.errors_check_label.pack(fill=X)
            self.errors_check_label_bool = True

    def apply(self):
        self.apply_button['state'] = DISABLED
        self.check_button['state'] = DISABLED
        self.z_value_enable['state'] = DISABLED
        self.map_listbox['state'] = DISABLED
        self.model_listbox['state'] = DISABLED
        self.x_value_entry['state'] = DISABLED
        self.x_value_entry2['state'] = DISABLED
        self.y_value_entry['state'] = DISABLED
        self.y_value_entry2['state'] = DISABLED
        self.z_value_entry['state'] = DISABLED
        self.select_map_directory['state'] = DISABLED

        for i in range(len(self.models_chosen)):
            self.model_entry_dict["entry" + str(i)]['state'] = DISABLED
        self.quantities = []
        self.x_random_model = []
        self.y_random_model = []
        for i in range(len(self.vars)):
            self.quantities.insert(i, self.vars[i].get())

        for i in range(len(self.models_chosen)):
            self.random_x = abs(int(self.x) - int(self.x2)) / int(self.quantities[i])
            print (self.random_x)
            self.x_random_model.insert(i, int(self.random_x))
        for i in range(len(self.models_chosen)):
            self.random_y = abs(int(self.y) - int(self.y2) / int(self.quantities[i]))
            self.y_random_model.insert(i, int(self.random_y))

        self.writefile = open(str(self.map_file_location), 'a')

        if not self.z_value_entry_active:
            for i in range(len(self.models_chosen)):
                k = 1
                while k <= int(self.quantities[i]):
                    a = randrange(int(self.x), int(self.x2))
                    b = randrange(int(self.y), int(self.y2))
                    self.writefile.write(("\n//Auto-Generated Model" + str(self.x_random_model[i])) + ("\n{\n") + ("\"origin\" \"%s %s 16.0\"\n") % (a, b) + ("\"model\" \"") + str(self.models_chosen[i]) + ("\"\n") + ("\"classname\" \"misc_model\"\n") + ("}"))
                    k += 1
        elif self.z_value_entry_active:
            for i in range(len(self.models_chosen)):
                k = 1
                while k <= int(self.quantities[i]):
                    a = randrange(int(self.x), int(self.x2))
                    b = randrange(int(self.y), int(self.y2))
                    self.writefile.write(("\n//Auto-Generated Model" + str(self.x_random_model[i])) + ("\n{\n") + ("\"origin\" \"%s %s %s\"\n") % (a, b, self.z) + ("\"model\" \"") + str(self.models_chosen[i]) + ("\"\n") + ("\"classname\" \"misc_model\"\n") + ("}"))
                    k += 1
        self.writefile.close()

        self.exit_button['state'] = NORMAL

root = Tk()
root.geometry("642x660")
root.wm_iconbitmap("icon.ico")
app = MainWindow(root)
root.mainloop()

 

I recommend only to look at the source for a general aspect for using Tkinter as the layout isn't actually the best and the algorithms aren't the best!

6

Share this post


Link to post
Share on other sites

For those who don't notice, the Source-code is inside the second spoiler. The video is in the first spoiler. I will update screenshots of the options and whatnot after. 

0

Share this post


Link to post
Share on other sites

I myself is not a map developer but I think this well come handy for those! And nice including the source code. :D

0

Share this post


Link to post
Share on other sites

didn't rycoon make this? I could have sworn he showed me this like half a year ago...

0

Share this post


Link to post
Share on other sites

didn't rycoon make this? I could have sworn he showed me this like half a year ago...

"As i have seen Phaedrean's video it inspired me to also create a fully functioning python script with additional features"

 

it has the same purpose, though it's going to have more features and working on GUI atm.

0

Share this post


Link to post
Share on other sites

"As i have seen Phaedrean's video it inspired me to also create a fully functioning python script with additional features"

ahhh sorry

I skimmed your post if you can't tell

0

Share this post


Link to post
Share on other sites

Thread contains updates - TL;DR Now has GUI, will release once finished coding and cleaned code. 

 

-Phelix

0

Share this post


Link to post
Share on other sites

Last update, sorry for double posting. Updated thread - Completed GUI, made it into a executable and standard Python format, little update on why i won't carry on the development.

 

 

As mentioned the program is sloppy in terms of the logic and functionality, though it does the job! 

1

Share this post


Link to post
Share on other sites

Updated application to new version.

Main changes:

- Better GUI
- Co-ordinates of old program was only from 0 so couldn't include minus numbers, now you can.
- Added Z co-ordinate
- Auto finds directory and inputs maps/models available
- Contains listboxes with scroller
- Can now use multiple models
- Error checking is more advanced

 

 

Another update will be coming soon as the the model max value is set to 10

1

Share this post


Link to post
Share on other sites

Updated application to new version.

Main changes:

- Better GUI

- Co-ordinates of old program was only from 0 so couldn't include minus numbers, now you can.

- Added Z co-ordinate

- Auto finds directory and inputs maps/models available

- Contains listboxes with scroller

- Can now use multiple models

- Error checking is more advanced

 

 

Another update will be coming soon as the the model max value is set to 10

The wonders of tkinter.

0

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now
Sign in to follow this  
Followers 0