🎮ArcadeLab

Untitled Game

by FlashMaker22
765 lines20.2 KB
▶ Play
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.UI;

public class BorderOpsPrototype : MonoBehaviour
{
    public static BorderOpsPrototype Instance { get; private set; }

    public CurrencyManager currency;
    public InventoryManager inventory;
    public TradeManager trade;
    public OperatorManager operators;
    public GunsmithManager gunsmith;
    public RangeManager range;
    public HeadquartersManager headquarters;
    public ServerAuthority server;

    [Header("主界面面板")]
    public GameObject mainPanel;
    public GameObject inventoryPanel;
    public GameObject tradePanel;
    public GameObject rangePanel;
    public GameObject gunsmithPanel;
    public GameObject operatorPanel;
    public GameObject headquartersPanel;

    [Header("主界面文本")]
    public Text hafuCoinText;
    public Text actionPointsText;
    public Text currentOperatorText;

    [Header("仓库")]
    public Transform warehouseListParent;
    public GameObject warehouseItemPrefab;
    public Text warehouseHint;

    [Header("交易行")]
    public InputField tradeSearchInput;
    public Transform marketListParent;
    public Transform myListingsParent;
    public GameObject tradeListingPrefab;
    public Text tradeBalanceText;

    [Header("靶场")]
    public Text rangeStatusText;
    public Button staticTargetButton;
    public Button movingTargetButton;
    public Button damageTestButton;
    public Button exitRangeButton;

    [Header("改枪台")]
    public Transform gunsmithBuildListParent;
    public GameObject gunsmithBuildPrefab;
    public Text gunsmithHint;

    [Header("干员")]
    public Transform operatorListParent;
    public GameObject operatorEntryPrefab;
    public Text currentOperatorDetailText;

    [Header("特勤处")]
    public Text headquartersLevelText;
    public Transform departmentListParent;
    public GameObject departmentEntryPrefab;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }

        Instance = this;

        currency = GetComponent<CurrencyManager>() ?? gameObject.AddComponent<CurrencyManager>();
        inventory = GetComponent<InventoryManager>() ?? gameObject.AddComponent<InventoryManager>();
        trade = GetComponent<TradeManager>() ?? gameObject.AddComponent<TradeManager>();
        operators = GetComponent<OperatorManager>() ?? gameObject.AddComponent<OperatorManager>();
        gunsmith = GetComponent<GunsmithManager>() ?? gameObject.AddComponent<GunsmithManager>();
        range = GetComponent<RangeManager>() ?? gameObject.AddComponent<RangeManager>();
        headquarters = GetComponent<HeadquartersManager>() ?? gameObject.AddComponent<HeadquartersManager>();
        server = GetComponent<ServerAuthority>() ?? gameObject.AddComponent<ServerAuthority>();
    }

    private void Start()
    {
        RefreshMainInfo();

        currency.onHafuCoinChanged.AddListener(_ => RefreshMainInfo());
        currency.onActionPointsChanged.AddListener(_ => RefreshMainInfo());

        operators.onOperatorChanged.AddListener(op =>
        {
            if (currentOperatorText != null && op != null)
                currentOperatorText.text = op.operatorName;
        });

        inventory.onInventoryChanged.AddListener(RefreshWarehouse);
        trade.onMarketUpdated.AddListener(RefreshMarket);
        trade.onMyListingsUpdated.AddListener(RefreshMyListings);
        currency.onHafuCoinChanged.AddListener(UpdateTradeBalance);
        range.onRangeStateChanged.AddListener(_ => UpdateRangeStatus());
        headquarters.onHeadquartersUpdated.AddListener(RefreshHeadquarters);

        RefreshWarehouse();
        RefreshMarket();
        RefreshMyListings();
        UpdateTradeBalance(currency.hafuCoin);
        UpdateRangeStatus();
        RefreshHeadquarters();
        RefreshOperators();
        RefreshGunsmithBuilds();
    }

    public void RefreshMainInfo()
    {
        if (hafuCoinText != null)
            hafuCoinText.text = $"哈夫币:{currency.hafuCoin}";

        if (actionPointsText != null)
            actionPointsText.text = $"行动点:{currency.actionPoints}";

        var op = operators.currentOperator;
        if (currentOperatorText != null && op != null)
            currentOperatorText.text = op.operatorName;
    }

    public void OpenInventory()
    {
        SetActivePanel(inventoryPanel);
        RefreshWarehouse();
    }

    public void OpenTrade()
    {
        SetActivePanel(tradePanel);
        RefreshMarket();
        RefreshMyListings();
        UpdateTradeBalance(currency.hafuCoin);
    }

    public void OpenRange()
    {
        SetActivePanel(rangePanel);
        range.EnterRange();
    }

    public void OpenGunsmith()
    {
        SetActivePanel(gunsmithPanel);
        RefreshGunsmithBuilds();
    }

    public void OpenOperators()
    {
        SetActivePanel(operatorPanel);
        RefreshOperators();
    }

    public void OpenHeadquarters()
    {
        SetActivePanel(headquartersPanel);
        RefreshHeadquarters();
    }

    public void StartOperation()
    {
        Debug.Log("进入对局:搜打撤模式");
    }

    public void BackToMain()
    {
        mainPanel?.SetActive(true);
        inventoryPanel?.SetActive(false);
        tradePanel?.SetActive(false);
        rangePanel?.SetActive(false);
        gunsmithPanel?.SetActive(false);
        operatorPanel?.SetActive(false);
        headquartersPanel?.SetActive(false);

        range.ExitRange();
    }

    private void SetActivePanel(GameObject panel)
    {
        mainPanel?.SetActive(false);
        inventoryPanel?.SetActive(false);
        tradePanel?.SetActive(false);
        rangePanel?.SetActive(false);
        gunsmithPanel?.SetActive(false);
        operatorPanel?.SetActive(false);
        headquartersPanel?.SetActive(false);

        if (panel != null)
            panel.SetActive(true);
    }

    public void RefreshWarehouse()
    {
        if (warehouseListParent == null) return;

        foreach (Transform child in warehouseListParent)
            Destroy(child.gameObject);

        foreach (var item in inventory.warehouse)
        {
            if (item.item == null) continue;

            var entry = CreateEntry(warehouseListParent, warehouseItemPrefab);
            var text = entry.GetComponentInChildren<Text>();
            if (text != null)
                text.text = $"{item.item.itemName} x{item.count}";
        }

        if (warehouseHint != null)
            warehouseHint.text = $"仓库物品数:{inventory.warehouse.Count}";
    }

    public void RefreshMarket()
    {
        FillTradeList(marketListParent, trade.marketListings, true);
    }

    public void RefreshMyListings()
    {
        FillTradeList(myListingsParent, trade.myListings, false);
    }

    private void FillTradeList(Transform parent, List<TradeListing> listings, bool isBuy)
    {
        if (parent == null || listings == null) return;

        foreach (Transform child in parent)
            Destroy(child.gameObject);

        foreach (var listing in listings)
        {
            if (listing.item == null) continue;

            var entry = CreateEntry(parent, tradeListingPrefab);
            var text = entry.GetComponentInChildren<Text>();
            var button = entry.GetComponentInChildren<Button>();

            if (text != null)
                text.text = $"{listing.item.itemName} x{listing.count} 单价:{listing.pricePerUnit}";

            if (button != null && isBuy)
            {
                string id = listing.listingId;
                button.onClick.AddListener(() => trade.TryBuy(id));
            }
        }
    }

    public void TradeSearch()
    {
        string keyword = tradeSearchInput != null ? tradeSearchInput.text : "";
        var results = trade.Search(keyword);
        FillTradeList(marketListParent, results, true);
    }

    private void UpdateTradeBalance(int coin)
    {
        if (tradeBalanceText != null)
            tradeBalanceText.text = $"哈夫币:{coin}";
    }

    public void UpdateRangeStatus()
    {
        if (rangeStatusText != null)
            rangeStatusText.text = range.inRange ? "靶场训练中" : "未在靶场";
    }

    public void EnterStaticTarget()
    {
        Debug.Log("靶场:静态靶已开启");
    }

    public void EnterMovingTarget()
    {
        Debug.Log("靶场:移动靶已开启");
    }

    public void EnterDamageTest()
    {
        Debug.Log("靶场:伤害测试已开启");
    }

    public void ExitRange()
    {
        range.ExitRange();
        BackToMain();
    }

    public void RefreshGunsmithBuilds()
    {
        if (gunsmithBuildListParent == null) return;

        foreach (Transform child in gunsmithBuildListParent)
            Destroy(child.gameObject);

        foreach (var build in gunsmith.savedBuilds)
        {
            if (build.gun == null) continue;

            var entry = CreateEntry(gunsmithBuildListParent, gunsmithBuildPrefab);
            var text = entry.GetComponentInChildren<Text>();
            if (text != null)
                text.text = $"{build.buildName} - {build.gun.gunName}";
        }

        if (gunsmithHint != null)
            gunsmithHint.text = $"已保存方案:{gunsmith.savedBuilds.Count}";
    }

    public void RefreshOperators()
    {
        if (operatorListParent == null) return;

        foreach (Transform child in operatorListParent)
            Destroy(child.gameObject);

        foreach (var op in operators.allOperators)
        {
            if (op == null) continue;

            var entry = CreateEntry(operatorListParent, operatorEntryPrefab);
            var text = entry.GetComponentInChildren<Text>();
            var button = entry.GetComponentInChildren<Button>();

            if (text != null)
                text.text = $"{op.operatorName} [{op.operatorClass}]";

            if (button != null)
            {
                var selected = op;
                button.onClick.AddListener(() =>
                {
                    operators.SelectOperator(selected);
                    if (currentOperatorDetailText != null)
                        currentOperatorDetailText.text = $"{selected.operatorName}\n{selected.description}";
                });
            }
        }
    }

    public void RefreshHeadquarters()
    {
        if (headquartersLevelText != null)
            headquartersLevelText.text = $"特勤处等级:{headquarters.headquartersLevel}";

        if (departmentListParent == null) return;

        foreach (Transform child in departmentListParent)
            Destroy(child.gameObject);

        var depts = new[]
        {
            headquarters.commandCenter,
            headquarters.trainingCenter,
            headquarters.rangeDept,
            headquarters.workshop,
            headquarters.gunsmithDept,
            headquarters.warehouseDept
        };

        foreach (var dept in depts)
        {
            if (dept == null) continue;

            var entry = CreateEntry(departmentListParent, departmentEntryPrefab);
            var text = entry.GetComponentInChildren<Text>();
            var button = entry.GetComponentInChildren<Button>();

            if (text != null)
                text.text = $"{dept.name} Lv.{dept.level} 升级消耗:{dept.upgradeCost}";

            if (button != null)
            {
                var selected = dept;
                button.onClick.AddListener(() =>
                {
                    if (headquarters.TryUpgrade(selected))
                        RefreshHeadquarters();
                });
            }
        }
    }

    private static GameObject CreateEntry(Transform parent, GameObject prefab)
    {
        if (prefab != null)
            return Instantiate(prefab, parent);

        var obj = new GameObject("Entry");
        obj.transform.SetParent(parent, false);
        return obj;
    }
}

[Serializable]
public class InventoryItem
{
    public ItemData item;
    public int count = 1;
    public int durability = 100;
}

[Serializable]
public class TradeListing
{
    public string listingId;
    public ItemData item;
    public int count;
    public int pricePerUnit;
    public string sellerId;
    public long createTime;
}

[Serializable]
public class GunBuild
{
    public GunData gun;
    public List<ItemData> attachments = new List<ItemData>();
    public string buildName;
}

[Serializable]
public class SkillData
{
    public string skillName;
    public string description;
    public float cooldown;
}

[Serializable]
public class Department
{
    public string name;
    public int level = 1;
    public int maxLevel = 10;
    public int upgradeCost = 1000;
}

public enum ItemType
{
    Ammo,
    Armor,
    Helmet,
    Medic,
    Material,
    Keycard,
    Collectible,
    Attachment,
    Gun,
    Backpack,
    ChestRig,
    SafeBox
}

public enum GunType
{
    AR,
    SMG,
    Sniper,
    Shotgun,
    DMR,
    LMG,
    Pistol
}

public enum OperatorClass
{
    Assault,
    Support,
    Recon,
    Engineer
}

[CreateAssetMenu(menuName = "BorderOps/ItemData")]
public class ItemData : ScriptableObject
{
    public string id;
    public string itemName;
    public ItemType type;
    public int stackSize = 1;
    public bool tradable = true;
    public bool bound = false;
    public int basePrice = 0;
    public Sprite icon;
}

[CreateAssetMenu(menuName = "BorderOps/GunData")]
public class GunData : ScriptableObject
{
    public string id;
    public string gunName;
    public GunType gunType;
    public float damage;
    public float fireRate;
    public float recoil;
    public float adsSpeed;
    public float mobility;
    public int magazineSize;
    public Sprite icon;
}

[CreateAssetMenu(menuName = "BorderOps/OperatorData")]
public class OperatorData : ScriptableObject
{
    public string id;
    public string operatorName;
    public OperatorClass operatorClass;
    public string description;
    public Sprite portrait;
    public SkillData passive;
    public SkillData tactical1;
    public SkillData tactical2;
    public SkillData ultimate;
}

public class CurrencyManager : MonoBehaviour
{
    public int hafuCoin = 10000;
    public int actionPoints = 5;

    public UnityAction<int> onHafuCoinChanged;
    public UnityAction<int> onActionPointsChanged;

    public void AddHafuCoin(int amount)
    {
        hafuCoin += amount;
        onHafuCoinChanged?.Invoke(hafuCoin);
    }

    public bool TrySpendHafuCoin(int amount)
    {
        if (hafuCoin < amount) return false;

        hafuCoin -= amount;
        onHafuCoinChanged?.Invoke(hafuCoin);
        return true;
    }

    public bool TrySpendActionPoints(int amount)
    {
        if (actionPoints < amount) return false;

        actionPoints -= amount;
        onActionPointsChanged?.Invoke(actionPoints);
        return true;
    }
}

public class InventoryManager : MonoBehaviour
{
    public List<InventoryItem> warehouse = new List<InventoryItem>();
    public List<InventoryItem> safeBox = new List<InventoryItem>();
    public List<InventoryItem> equippedGear = new List<InventoryItem>();

    public UnityAction onInventoryChanged;

    public void AddItem(ItemData item, int count = 1)
    {
        if (item == null) return;

        if (item.stackSize > 1)
        {
            var existing = warehouse.Find(x => x.item == item);
            if (existing != null)
            {
                existing.count += count;
            }
            else
            {
                warehouse.Add(new InventoryItem { item = item, count = count });
            }
        }
        else
        {
            for (int i = 0; i < count; i++)
            {
                warehouse.Add(new InventoryItem { item = item, count = 1 });
            }
        }

        onInventoryChanged?.Invoke();
    }

    public bool RemoveItem(ItemData item, int count = 1)
    {
        if (item == null) return false;

        var existing = warehouse.Find(x => x.item == item && x.count >= count);
        if (existing == null) return false;

        existing.count -= count;
        if (existing.count <= 0)
        {
            warehouse.Remove(existing);
        }

        onInventoryChanged?.Invoke();
        return true;
    }

    public List<InventoryItem> GetTradableItems()
    {
        return warehouse.FindAll(x => x.item != null && x.item.tradable && !x.item.bound);
    }
}

public class TradeManager : MonoBehaviour
{
    public List<TradeListing> marketListings = new List<TradeListing>();
    public List<TradeListing> myListings = new List<TradeListing>();

    public float feeRate = 0.05f;
    public int maxListings = 10;

    public UnityAction onMarketUpdated;
    public UnityAction onMyListingsUpdated;

    public bool TryList(InventoryItem inventoryItem, int count, int pricePerUnit)
    {
        if (inventoryItem == null || inventoryItem.item == null) return false;
        if (!inventoryItem.item.tradable) return false;
        if (myListings.Count >= maxListings) return false;
        if (pricePerUnit <= 0) return false;
        if (count <= 0 || count > inventoryItem.count) return false;

        if (!BorderOpsPrototype.Instance.inventory.RemoveItem(inventoryItem.item, count))
            return false;

        var listing = new TradeListing
        {
            listingId = Guid.NewGuid().ToString(),
            item = inventoryItem.item,
            count = count,
            pricePerUnit = pricePerUnit,
            sellerId = "player",
            createTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds()
        };

        myListings.Add(listing);
        marketListings.Add(listing);

        onMyListingsUpdated?.Invoke();
        onMarketUpdated?.Invoke();
        return true;
    }

    public bool TryBuy(string listingId)
    {
        var listing = marketListings.Find(x => x.listingId == listingId);
        if (listing == null) return false;

        int total = listing.pricePerUnit * listing.count;
        if (!BorderOpsPrototype.Instance.currency.TrySpendHafuCoin(total)) return false;

        BorderOpsPrototype.Instance.inventory.AddItem(listing.item, listing.count);
        marketListings.Remove(listing);

        if (listing.sellerId == "player")
        {
            myListings.Remove(listing);
            onMyListingsUpdated?.Invoke();
        }

        onMarketUpdated?.Invoke();
        return true;
    }

    public List<TradeListing> Search(string keyword, ItemType? type = null)
    {
        var result = marketListings;

        if (!string.IsNullOrEmpty(keyword))
        {
            result = result.FindAll(x =>
                x.item != null &&
                x.item.itemName.IndexOf(keyword, StringComparison.OrdinalIgnoreCase) >= 0);
        }

        if (type.HasValue)
        {
            result = result.FindAll(x => x.item != null && x.item.type == type.Value);
        }

        return result;
    }
}

public class OperatorManager : MonoBehaviour
{
    public List<OperatorData> allOperators = new List<OperatorData>();
    public OperatorData currentOperator;

    public UnityAction<OperatorData> onOperatorChanged;

    public void SelectOperator(OperatorData op)
    {
        if (op == null) return;

        currentOperator = op;
        onOperatorChanged?.Invoke(currentOperator);
    }
}

public class GunsmithManager : MonoBehaviour
{
    public List<GunBuild> savedBuilds = new List<GunBuild>();

    public void SaveBuild(GunBuild build)
    {
        if (build == null || build.gun == null) return;

        var existing = savedBuilds.Find(x => x.buildName == build.buildName);
        if (existing != null)
        {
            existing.gun = build.gun;
            existing.attachments = new List<ItemData>(build.attachments);
        }
        else
        {
            savedBuilds.Add(build);
        }
    }

    public void RemoveBuild(string buildName)
    {
        savedBuilds.RemoveAll(x => x.buildName == buildName);
    }
}

public class RangeManager : MonoBehaviour
{
    public bool inRange = false;

    public UnityAction<bool> onRangeStateChanged;

    public void EnterRange()
    {
        inRange = true;
        onRangeStateChanged?.Invoke(true);
        Debug.Log("已进入靶场");
    }

    public void ExitRange()
    {
        inRange = false;
        onRangeStateChanged?.Invoke(false);
        Debug.Log("已退出靶场");
    }
}

public class HeadquartersManager : MonoBehaviour
{
    public int headquartersLevel =

[(doc_common_card_1)]

Game Source: Untitled Game

Creator: FlashMaker22

Libraries: none

Complexity: complex (765 lines, 20.2 KB)

The full source code is displayed above on this page.

Remix Instructions

To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: untitled-game-flashmaker22" to link back to the original. Then publish at arcadelab.ai/publish.