কীভাবে অনেকগুলি অনন্য অস্ত্র / বানান / শক্তির কোড গঠন করবেন


22

আমি একটি অনভিজ্ঞ প্রোগ্রামার , পাইথন ব্যবহার করে এফটিএল শিরাতে একটি "রোগেলাইক-জাতীয়" গেম তৈরি করছি (পাইগামের এখনও কোনও বিষয় নেই যা আমি এখনও কেবলমাত্র পাঠ্যের সাথে সম্পর্কিত আছি)।

আমার গেমটিতে প্রচুর পরিমাণে অস্ত্র (শুরুতে 50 এর কাছাকাছি) থাকবে যা অনন্য ক্ষমতা অর্জন করে। আমি কীভাবে অবজেক্ট কোডটিকে শক্তিশালী (অস্ত্রগুলিকে মূলত পৃথকভাবে প্রভাব ফেলতে দেয়ার ক্ষেত্রে) এবং এক্সটেনসিবল (যাতে আমি পরে আরও অস্ত্র যোগ করতে পারি উদাহরণস্বরূপ, যাতে সেগুলি ফোল্ডারে ফেলে দেওয়া যায়) এমনভাবে কীভাবে গঠন করা যায় তা বোঝার জন্য আমি সংগ্রাম করছি )।

আমার প্রথম প্রবৃত্তিটি ছিল বেসিকওয়াপন ক্লাস এবং সেই শ্রেণি থেকে বিভিন্ন অস্ত্র উত্তরাধিকারী হওয়া। যাইহোক, এটি আমার কাছে সমস্যাযুক্ত বলে মনে হচ্ছে: হয় আমাকে বেসিক উইপন ক্লাসটি এতটাই নগ্ন হয়ে যেতে হবে যে এটি মূলত অকেজো (সমস্ত অস্ত্রের মধ্যে সাধারণ বৈশিষ্ট্যগুলির মধ্যে নাম এবং টাইপ (পিস্তল, কুড়াল ইত্যাদি)), বা আমার প্রতিটি ভবিষ্যদ্বাণী করতে হবে অনন্য প্রভাব আমি কখনই এর সাথে আসব এবং কোডটি বেসিকওয়াপনে প্রবেশ করব।

দ্বিতীয়টি স্পষ্টত অসম্ভব তবে পূর্বেরটি এখনও কাজ করা যায়। তবে, এই প্রশ্নটি আমাকে ছেড়ে দিয়েছে: আমি পৃথক অস্ত্রের কোডটি কোথায় রাখি?

আমি কি প্লাজমারিফল.পি, রকেটলাঞ্চার.পি, স্বারমফবিস.পি, ইত্যাদি তৈরি করে এগুলিকে এমন কোনও ফোল্ডারে ফেলে দিই যেখানে গেমগুলি সেগুলি আমদানি করতে পারে?

বা ডাটাবেস-স্টাইলের ফাইল রয়েছে (সম্ভবত এক্সেল স্প্রেডশিটের মতো সাধারণ কিছু) এতে প্রতিটি অস্ত্রের জন্য কোনওরকম অনন্য কোড রয়েছে - এটি খোলার / নির্বাহের অবলম্বন না করেই?

পরবর্তী সমাধান (ডাটাবেস) এর ক্ষেত্রে, আমি মনে করি যে মূল সমস্যাটির সাথে আমি লড়াই করছি তা হ'ল আমি যখন বুঝতে পারি যে কোড এবং ডেটার মধ্যে বিচ্ছিন্নতা বজায় রাখা বাঞ্ছনীয়, তখন আমি অস্ত্রগুলি "কোড" এর মধ্যে লাইনটিকে অস্পষ্ট করার মতো অনুভব করি এবং "ডেটা" কিছুটা; এগুলি খেলায় পাওয়া যায় এমন বিভিন্ন ধরণের অনুরূপ জিনিসের প্রতিনিধিত্ব করে, যে অর্থে তারা ডেটার মতো হয় তবে তাদের বেশিরভাগের জন্য কমপক্ষে কিছু অনন্য কোডের প্রয়োজন হয় যা অন্য কোনও আইটেমের সাথে ভাগ করা হয় না, এই অর্থে তারা স্বাভাবিকভাবেই, কোড।

এই সাইটে আমি অন্য কোথাও খুঁজে পেয়েছি একটি আংশিক সমাধান বেসিকওয়াপন ক্লাসকে খালি পদ্ধতিগুলির একটি গোছা - অন_াউন্ড_স্টার্ট (), অন_ট্যাক (), অন_মোভ () ইত্যাদি - এবং তারপরে প্রতিটি অস্ত্রের জন্য সেই পদ্ধতিগুলিকে ওভাররাইড করার পরামর্শ দেয়। যুদ্ধচক্রের প্রাসঙ্গিক পর্যায়ে, গেমটি প্রতিটি চরিত্রের অস্ত্রের জন্য উপযুক্ত পদ্ধতিটিকে কল করবে এবং কেবলমাত্র পদ্ধতিতে সংজ্ঞায়িতভাবে কিছু করা হবে। এটি সহায়তা করে, তবে এটি এখনও আমাকে জানায় না যে প্রতিটি অস্ত্রের জন্য আমাকে কোড এবং / অথবা ডেটা কোথায় রেখে দিতে হবে।

সেখানে কি আলাদা ভাষা বা সরঞ্জাম আছে যা আমি অর্ধ-ডেটা, অর্ধেক কোডের চিমেরা বাছাই হিসাবে ব্যবহার করতে পারি? আমি কি পুরোপুরি ভাল প্রোগ্রামিং অনুশীলন কসাই করছি?

আমার ওওপি সম্পর্কে উপলব্ধি সর্বোত্তমভাবে স্কেচিযুক্ত, সুতরাং আমি যে প্রতিক্রিয়াগুলি খুব বেশি কম্পিউটার বিজ্ঞান-ওয়াই না তাদের প্রশংসা করব ।

সম্পাদনা: ভন হিল্টস নীচে তার পোস্টে পরিষ্কার করে দিয়েছেন যে আমি মূলত যা বলছি তা ডেটাচালিত প্রোগ্রামিং। আমার প্রশ্নের সারমর্মটি হ'ল: কীভাবে আমি ডেটা চালিত ডিজাইনটি এমনভাবে প্রয়োগ করতে পারি যাতে ডেটা স্ক্রিপ্টগুলি ধারণ করতে পারে এবং মূল প্রোগ্রামের কোডটি পরিবর্তন না করেই নতুন অস্ত্রগুলিকে নতুন জিনিস করতে সক্ষম করে?



@ বাইট 56 সম্পর্কিত; তবে আমি মনে করি ওপি এটি এড়াতে চাইছে। আমি মনে করি তারা আরও ডেটা চালিত পদ্ধতির সন্ধান করার চেষ্টা করছে। আমি ভুল হলে শুধরে.
ভন হিলস

আমি সম্মত তারা আরও ডেটা ওরিয়েন্টেড পদ্ধতির সন্ধান করার চেষ্টা করছে। বিশেষ করে, আমি যে প্রশ্নের জোশ এর উত্তর মত: gamedev.stackexchange.com/a/17286/7191
MichaelHouse

আহ, সে সম্পর্কে দুঃখিত। :) আমার "গৃহীত উত্তর" পড়ার অভ্যাস আছে।
ভন হিলস

উত্তর:


17

আপনার গেমটি সম্পূর্ণরূপে অপ্রত্যাশিত এবং / বা পদ্ধতিটিতে তৈরি হওয়া প্রক্রিয়াজাত না হয়ে থাকলে আপনি অবশ্যই ডেটা-চালিত পদ্ধতিটি চান ।

মূলত, এর মধ্যে আপনার অস্ত্র সম্পর্কিত তথ্য একটি মার্কআপ ভাষা বা আপনার পছন্দের ফাইল ফর্ম্যাটে সংরক্ষণ করা জড়িত। এক্সএমএল এবং জেএসএন উভয়ই ভাল, পঠনযোগ্য পছন্দ যা আপনি কেবলমাত্র দ্রুত শুরু করার চেষ্টা করলে জটিল সম্পাদকদের প্রয়োজন ছাড়াই সম্পাদনা মোটামুটি সহজ করতে ব্যবহার করা যেতে পারে। ( এবং পাইথন এক্সএমএলকে খুব সহজেই পার্স করতে পারে! ) আপনি 'পাওয়ার', 'প্রতিরক্ষা', 'ব্যয়', এবং 'পরিসংখ্যান' এর মতো বৈশিষ্ট্যগুলি সেট করে যা প্রাসঙ্গিক। আপনি আপনার ডেটা গঠনের উপায়টি আপনার উপর নির্ভর করবে।

যদি কোনও অস্ত্রের স্থিতির প্রভাব যুক্ত করার প্রয়োজন হয় তবে এটিকে একটি স্ট্যাটাস এফেক্ট নোড দিন এবং তারপরে অন্য কোনও ডেটা-চালিত বস্তুর মাধ্যমে স্থিতির প্রভাবের প্রভাবগুলি নির্দিষ্ট করুন specify এটি আপনার কোডটিকে নির্দিষ্ট গেমের উপর কম নির্ভরশীল করে তুলবে এবং আপনার গেমের সম্পাদনা এবং পরীক্ষাটি তুচ্ছ করে তুলবে। সব সময় পুনরায় সংযোগ না করাও একটি বোনাস।

সম্পূরক পাঠ্য নীচে পাওয়া যায়:


2
কম্পোনেন্ট ভিত্তিক সিস্টেমের মতো, যেখানে স্ক্রিপ্টগুলির মাধ্যমে উপাদানগুলি পড়া হয়। ভালো লেগেছে এই: gamedev.stackexchange.com/questions/33453/...
MichaelHouse

2
এবং আপনি যখন এটির সময়ে থাকবেন তখন সেই ডেটার একটি স্ক্রিপ্ট অংশ তৈরি করুন যাতে নতুন অস্ত্র কোনও মূল কোড পরিবর্তন না করে নতুন জিনিস করতে পারে।
প্যাট্রিক হিউজেস

@ ওয়াঘান হিল্টস: আপনাকে ধন্যবাদ, ডেটা-চালিত মনে হয় হুবহু আমি বুঝি যে আমার প্রয়োজনীয়তা ছিল। আমার এখনও উত্তর প্রয়োজন হওয়ায় আমি আরও কিছুক্ষণের জন্য প্রশ্নটি খোলা রাখছি, তবে সম্ভবত এটি সেরা উত্তর হিসাবে বেছে নেবে।
হেনরেবোথা

@ পেট্রিক হিউজেস: আমি ঠিক তাই চাই! আমি কেমন করে ঐটি করি? আপনি কি আমাকে একটি সাধারণ উদাহরণ বা টিউটোরিয়াল দেখাতে পারেন?
হেনরেবোথা

1
প্রথমে আপনার ইঞ্জিনে একটি স্ক্রিপ্ট ইঞ্জিন প্রয়োজন, অনেক লোক LUA পছন্দ করে যা প্রভাব এবং পরিসংখ্যানের মতো গেমপ্লে সিস্টেমগুলিতে অ্যাক্সেস করে। তারপরে আপনি যেহেতু ডেটা বর্ণনা থেকে ইতিমধ্যে আপনার বস্তুগুলি পুনরুদ্ধার করছেন আপনি যখনই নতুন ইঞ্জিনটি সক্রিয় করবেন তখনই আপনি যে ইঞ্জিনটি কল করেন সেই স্ক্রিপ্টটি এম্বেড করতে পারবেন। MUD- র পুরানো দিনগুলিতে একে "প্রোক", (প্রক্রিয়াটির জন্য সংক্ষিপ্ত) বলা হত। হার্ড অংশটি আপনার গেমপ্লে বৈশিষ্ট্যগুলি ইঞ্জিনে যথেষ্ট নমনীয় করে তুলছে বাইরে থেকে ডাকতে এবং যথেষ্ট বৈশিষ্ট্য সহ।
প্যাট্রিক হিউজেস

6

(আমি একটি মন্তব্যের পরিবর্তে উত্তর জমা দেওয়ার জন্য দুঃখিত, তবে আমার কাছে এখনও কোনও উত্তর নেই))

ভনের উত্তর দুর্দান্ত তবে আমি আমার দুটি সেন্ট যুক্ত করতে চাই।

আপনি এক্সএমএল বা জেএসএন ব্যবহার করতে এবং রানটাইমটিতে পার্স করতে চান তার অন্যতম প্রধান কারণ কোডটি পুনরায় সংকলন না করেই নতুন মানগুলির পরিবর্তন এবং পরীক্ষা করা। পাইথনকে যেমন ব্যাখ্যা করা হয় এবং আমার মতে, বেশ পাঠযোগ্য, আপনার কাছে একটি অভিধানে এবং অভিধানে সবকিছু সাজানো ফাইলের কাঁচা তথ্য থাকতে পারে:

weapons = {
           'megaLazer' : {
                          'name' : "Mega Lazer XPTO"
                          'damage' : 100
                       },
           'ultraCannon' : {
                          'name' : "Ultra Awesome Cannon",
                          'damage' : 200
                       }
          }

আপনি কেবল ফাইল / মডিউলটি আমদানি করুন এবং এটিকে একটি সাধারণ অভিধান হিসাবে ব্যবহার করুন।

আপনি যদি স্ক্রিপ্টগুলি যুক্ত করতে চান তবে আপনি পাইথনের গতিশীল প্রকৃতি এবং প্রথম শ্রেণির ফাংশন ব্যবহার করতে পারেন। আপনি এরকম কিছু করতে পারেন:

def special_shot():
    ...

weapons = { 'megalazer' : { ......
                            shoot_gun = special_shot
                          }
          }

যদিও আমি বিশ্বাস করি যে এটি ডেটা চালিত ডিজাইনের বিরুদ্ধে থাকবে। ১০০% ডিডিডি হওয়ার জন্য আপনার কাছে ইনফোমেশন (ডেটা) রয়েছে যা নির্দিষ্ট করে যে অস্ত্রটি ব্যবহার করবে সেগুলি কী করবে তা এবং কোডটি কী তা নির্দিষ্ট করে। কার্যকারিতার সাথে ডেটা মেশানো না হওয়ায় আপনি এইভাবে ডিডিডি ভাঙবেন না।


ধন্যবাদ. কেবল একটি সাধারণ কোড উদাহরণ দেখে এটি ক্লিক করতে সহায়তা করে।
হেনরেবোথা

1
চমৎকার উত্তরের জন্য +1 এবং আপনার মন্তব্য করার মতো যথেষ্ট পরিমাণে বিশ্লেষণ রয়েছে। ;) স্বাগত.
ver

4

ডেটা চালিত নকশা

আমি সম্প্রতি কোড রিভিউতে এই প্রশ্নের মতো কিছু জমা দিয়েছি ।

কিছু পরামর্শ এবং উন্নতির পরে, ফলাফলটি একটি সহজ কোড যা কোনও অভিধান (বা জেএসএন) এর উপর ভিত্তি করে অস্ত্র তৈরিতে কিছু আপেক্ষিক নমনীয়তার অনুমতি দেয়। ডেটা রানটাইম-এ ব্যাখ্যা করা হয় এবং Weaponএকটি সম্পূর্ণ স্ক্রিপ্ট অনুবাদকারীর উপর নির্ভর করার প্রয়োজন না করে সহজ যাচাই ক্লাস নিজেই করে থাকে।

পাইথন একটি দোহিত ভাষা হওয়া সত্ত্বেও ডেটা-চালিত ডিজাইন (উত্স এবং ডেটা ফাইল উভয়ই তাদের পুনরায় সংকলনের প্রয়োজন ছাড়াই সম্পাদনা করা যেতে পারে), যেমন আপনি উপস্থাপন করেছেন তেমন ক্ষেত্রে সঠিক কাজ করার মতো মনে হচ্ছে। এই প্রশ্নটি ধারণা সম্পর্কে আরও বিশদগুলিতে যায়, এর উপকারিতা এবং বিপরীতে। এটি সম্পর্কে কর্নেল বিশ্ববিদ্যালয়ের একটি দুর্দান্ত উপস্থাপনাও রয়েছে res

অন্যান্য ভাষার সাথে তুলনা করা, যেমন সি ++, সম্ভবত ডেটা এক্স ইঞ্জিনের ইন্টারঅ্যাকশন এবং স্ক্রিপ্টিং পরিচালনা করতে কোনও স্ক্রিপ্টিং ভাষা (যেমন এলইউএ) এবং ডেটা সংরক্ষণের জন্য একটি নির্দিষ্ট ডেটা ফর্ম্যাট (এক্সএমএলের মতো) ব্যবহার করতে পারে, পাইথন আসলেই করতে পারে এটি সমস্ত নিজস্বভাবে (মানকটি বিবেচনা করে dictতবে এটির weakrefপরে বিশেষত সম্পদ লোডিং এবং ক্যাশিংয়ের জন্য)।

একটি স্বাধীন বিকাশকারী, তবে, এই নিবন্ধে প্রস্তাবিত হিসাবে ডেটা চালিত পদ্ধতির চূড়ান্ত করতে না পারে :

আমি ডেটা-চালিত নকশা সম্পর্কে কত? আমি মনে করি না কোনও গেম ইঞ্জিনে গেম-নির্দিষ্ট কোডের একক লাইন থাকা উচিত। একটি না. কোনও হার্ডকোডযুক্ত অস্ত্রের ধরণ নেই। কোনও হার্ডকোডযুক্ত এইচইউডি বিন্যাস নেই। কোনও হার্ডকোডযুক্ত ইউনিট নয় AI নাদা। জিপ। Zilch।

পাইথনের সাহায্যে, উত্পাদনশীলতা এবং এক্সটেনসিবিলিটি উভয়ের লক্ষ্য রেখেই বস্তু-ভিত্তিক এবং ডেটা-চালিত উভয় পদ্ধতির মধ্যেই সেরা উপকার পেতে পারে।

সাধারণ নমুনা প্রক্রিয়াকরণ

কোড পর্যালোচনায় আলোচিত নির্দিষ্ট ক্ষেত্রে, একটি অভিধান "স্থিতিক বৈশিষ্ট্য" এবং ব্যাখ্যা করার জন্য যুক্তি উভয়ই সংরক্ষণ করে - যদি অস্ত্রটির কোনও শর্তযুক্ত আচরণ করা উচিত।

নীচের উদাহরণে একটি তরোয়ালটিতে 'অ্যান্টিপ্যালাদিন' শ্রেণীর চরিত্রের হাতে কিছু ক্ষমতা এবং পরিসংখ্যান থাকা উচিত এবং অন্যান্য চরিত্রের দ্বারা ব্যবহৃত যখন নিম্নতর পরিসংখ্যানের সাথে কোনও প্রভাব নেই):

WEAPONS = {
    "bastard's sting": {
        # magic enhancement, weight, value, dmg, and other attributes would go here.
        "magic": 2,

        # Those lists would contain the name of effects the weapon provides by default.
        # They are empty because, in this example, the effects are only available in a
        # specific condition.    
        "on_turn_actions": [],
        "on_hit_actions": [],
        "on_equip": [
            {
                "type": "check",
                "condition": {
                    'object': 'owner',
                    'attribute': 'char_class',
                    'value': "antipaladin"
                },
                True: [
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_hit",
                            "actions": ["unholy"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "add_to",
                        "args": {
                            "category": "on_turn",
                            "actions": ["unholy aurea"]
                        }
                    },
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 5
                        }
                    }
                ],
                False: [
                    {
                        "type": "action",
                        "action": "set_attribute",
                        "args": {
                            "field": "magic",
                            "value": 2
                        }
                    }
                ]
            }
        ],
        "on_unequip": [
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_hit",
                    "actions": ["unholy"]
                },
            },
            {
                "type": "action",
                "action": "remove_from",
                "args": {
                    "category": "on_turn",
                    "actions": ["unholy aurea"]
                },
            },
            {
                "type": "action",
                "action": "set_attribute",
                "args": ["magic", 2]
            }
        ]
    }
}

পরীক্ষার উদ্দেশ্যে, আমি সহজ Playerএবং Weaponক্লাস তৈরি করেছি : প্রথমে অস্ত্রটি ধরে রাখা / সজ্জিত করা (সুতরাং এটির শর্তসাপেক্ষে on_equip সেটিং বলা হয়) এবং পরবর্তীকৃত হিসাবে একক শ্রেণি হিসাবে অভিধান থেকে ডেটা পুনরুদ্ধার করতে পারে, আইটেমের নাম হিসাবে পাস করা হয়েছে Weaponআরম্ভের সময় যুক্তি । এগুলি সঠিক গেম ক্লাসের নকশা প্রতিফলিত করে না তবে তথ্যের পরীক্ষার জন্য এখনও কার্যকর হতে পারে:

class Player:
    """Represent the player character."""

    inventory = []

    def __init__(self, char_class):
        """For this example, we just store the class on the instance."""
        self.char_class = char_class

    def pick_up(self, item):
        """Pick an object, put in inventory, set its owner."""
        self.inventory.append(item)
        item.owner = self


class Weapon:
    """A type of item that can be equipped/used to attack."""

    equipped = False
    action_lists = {
        "on_hit": "on_hit_actions",
        "on_turn": "on_turn_actions",
    }

    def __init__(self, template):
        """Set the parameters based on a template."""
        self.__dict__.update(WEAPONS[template])

    def toggle_equip(self):
        """Set item status and call its equip/unequip functions."""
        if self.equipped:
            self.equipped = False
            actions = self.on_unequip
        else:
            self.equipped = True
            actions = self.on_equip

        for action in actions:
            if action['type'] == "check":
                self.check(action)
            elif action['type'] == "action":
                self.action(action)

    def check(self, dic):
        """Check a condition and call an action according to it."""
        obj = getattr(self, dic['condition']['object'])
        compared_att = getattr(obj, dic['condition']['attribute'])
        value = dic['condition']['value']
        result = compared_att == value

        self.action(*dic[result])

    def action(self, *dicts):
        """Perform action with args, both specified on dicts."""
        for dic in dicts:
            act = getattr(self, dic['action'])
            args = dic['args']
            if isinstance(args, list):
                act(*args)
            elif isinstance(args, dict):
                act(**args)

    def set_attribute(self, field, value):
        """Set the specified field with the given value."""
        setattr(self, field, value)

    def add_to(self, category, actions):
        """Add one or more actions to the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action not in action_list:
                action_list.append(action)

    def remove_from(self, category, actions):
        """Remove one or more actions from the category's list."""
        action_list = getattr(self, self.action_lists[category])

        for action in actions:
            if action in action_list:
                action_list.remove(action)

ভবিষ্যতের কিছু উন্নতির সাথে আমি আশা করি যে এটি আমার একদিন পুরো অস্ত্রের পরিবর্তে অস্ত্র উপাদানগুলি প্রক্রিয়াজাতকরণ, গতিশীল কারুকার্যকরণ সিস্টেমটিও চালিয়ে যাবে ...

পরীক্ষা

  1. অক্ষর একটি অস্ত্র বাছাই করে, এটি সজ্জিত করুন (আমরা এর পরিসংখ্যানগুলি মুদ্রণ করি), তারপরে এটি ফেলে দিন;
  2. চরিত্র বি একই অস্ত্রটিকে বেছে নিয়েছে, এটি সজ্জিত করে (এবং আমরা তার পরিসংখ্যানগুলি কীভাবে আলাদা তা দেখানোর জন্য এটি আবার মুদ্রণ করি)।

এটার মত:

def test():
    """A simple test.

    Item features should be printed differently for each player.
    """
    weapon = Weapon("bastard's sting")
    player1 = Player("bard")
    player1.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))
    weapon.toggle_equip()

    player2 = Player("antipaladin")
    player2.pick_up(weapon)
    weapon.toggle_equip()
    print("Enhancement: {}, Hit effects: {}, Other effects: {}".format(
        weapon.magic, weapon.on_hit_actions, weapon.on_turn_actions))

if __name__ == '__main__':
    test()

এটি মুদ্রণ করা উচিত:

একটি বার্ডের জন্য

বৃদ্ধি: 2, হিট প্রভাব: [], অন্যান্য প্রভাব: []

একটি অ্যান্টিপালাদিনের জন্য

বর্ধন: 5, হিট প্রভাব: ['অপরিষ্কার'], অন্যান্য প্রভাব: ['অপরিষ্কার অরিয়া']

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.