Beispiel #1
0
        public override async Task InputAsync(MainRepository repo, uint characterId, IEnumerable <GameDateTime> gameDates, params CharacterCommandParameter[] options)
        {
            var itemType     = (CharacterItemType)options.FirstOrDefault(p => p.Type == 1).Or(ErrorCode.LackOfCommandParameter).NumberValue;
            var resourceSize = options.FirstOrDefault(p => p.Type == 3)?.NumberValue;
            var chara        = await repo.Character.GetByIdAsync(characterId).GetOrErrorAsync(ErrorCode.LoginCharacterNotFoundError);

            var items = await repo.Character.GetItemsAsync(chara.Id);

            if (!items.Any(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterHold || i.Status == CharacterItemStatus.CharacterPending)))
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            var info = CharacterItemInfoes.Get(itemType).GetOrError(ErrorCode.InvalidCommandParameter);

            if (!info.CanSell)
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            if (info.IsResource)
            {
                if (resourceSize == null)
                {
                    ErrorCode.LackOfCommandParameter.Throw();
                }
                if (resourceSize > info.DefaultResource)
                {
                    ErrorCode.InvalidCommandParameter.Throw();
                }
            }

            await repo.CharacterCommand.SetAsync(characterId, this.Type, gameDates, options);
        }
        public static async Task <bool> ResultAsync(MainRepository repo, SystemData system, DelayEffect delay, Func <uint, string, Task> logAsync)
        {
            var chara = await repo.Character.GetByIdAsync(delay.CharacterId);

            var type = (CharacterItemType)delay.TypeData;

            if (!chara.HasData)
            {
                return(false);
            }
            if (chara.Data.HasRemoved)
            {
                return(true);
            }

            var generateInfo = GenerateItemInfo.GetInfo(chara.Data, type);

            if (generateInfo == null)
            {
                return(false);
            }

            if (delay.IntAppearGameDateTime + generateInfo.Length <= system.IntGameDateTime)
            {
                var info = CharacterItemInfoes.Get(type);
                if (info.HasData)
                {
                    var item = new CharacterItem
                    {
                        Type        = type,
                        Status      = CharacterItemStatus.CharacterPending,
                        CharacterId = chara.Data.Id,
                        IntLastStatusChangedGameDate = system.IntGameDateTime,
                    };

                    if (info.Data.IsResource)
                    {
                        item.Resource = (ushort)(info.Data.DefaultResource + RandomService.Next((int)(generateInfo.ResourceAttribute(chara.Data) * 1.4f)));
                    }

                    await ItemService.GenerateItemAndSaveAsync(repo, item);

                    await ItemService.SetCharacterPendingAsync(repo, item, chara.Data);

                    if (info.Data.IsResource)
                    {
                        await logAsync(chara.Data.Id, $"アイテム {info.Data.Name} を生産しました。資源量: <num>{item.Resource}</num>");
                    }
                    else
                    {
                        await logAsync(chara.Data.Id, $"アイテム {info.Data.Name} を生産しました");
                    }

                    return(true);
                }
            }

            return(false);
        }
Beispiel #3
0
        public override async Task InputAsync(MainRepository repo, uint characterId, IEnumerable <GameDateTime> gameDates, params CharacterCommandParameter[] options)
        {
            var itemType     = (CharacterItemType)options.FirstOrDefault(p => p.Type == 1).Or(ErrorCode.LackOfCommandParameter).NumberValue;
            var resourceSize = options.FirstOrDefault(p => p.Type == 4)?.NumberValue;
            var chara        = await repo.Character.GetByIdAsync(characterId).GetOrErrorAsync(ErrorCode.LoginCharacterNotFoundError);

            var town = await repo.Town.GetByIdAsync(chara.TownId).GetOrErrorAsync(ErrorCode.InternalDataNotFoundError, new { command = "item", townId = chara.TownId, });

            var items = await repo.Town.GetItemsAsync(town.Id);

            if (!items.Any(i => i.Status == CharacterItemStatus.TownOnSale && i.Type == itemType))
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            var info = CharacterItemInfoes.Get(itemType);

            if (!info.HasData)
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            if (info.Data.IsResource)
            {
                if (resourceSize == null)
                {
                    ErrorCode.LackOfCommandParameter.Throw();
                }
                if (resourceSize > info.Data.DefaultResource)
                {
                    ErrorCode.InvalidCommandParameter.Throw();
                }
            }

            var optionsWithoutResult = options.Where(o => o.Type == 1 || o.Type == 4);

            options = optionsWithoutResult.Append(new CharacterCommandParameter
            {
                Type        = 2,
                NumberValue = (int)chara.TownId,
            }).ToArray();

            await repo.CharacterCommand.SetAsync(characterId, this.Type, gameDates, options);
        }
        public override async Task InputAsync(MainRepository repo, uint characterId, IEnumerable <GameDateTime> gameDates, params CharacterCommandParameter[] options)
        {
            var itemType     = (CharacterItemType)options.FirstOrDefault(p => p.Type == 1).Or(ErrorCode.LackOfCommandParameter).NumberValue;
            var targetId     = (uint)options.FirstOrDefault(p => p.Type == 2).Or(ErrorCode.LackOfCommandParameter).NumberValue;
            var resourceSize = options.FirstOrDefault(p => p.Type == 4)?.NumberValue ?? 0;
            var chara        = await repo.Character.GetByIdAsync(characterId).GetOrErrorAsync(ErrorCode.LoginCharacterNotFoundError);

            var target = await repo.Character.GetByIdAsync(targetId).GetOrErrorAsync(ErrorCode.CharacterNotFoundError);

            if (target.HasRemoved)
            {
                ErrorCode.CharacterNotFoundError.Throw();
            }

            if (target.Id == chara.Id)
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            var items = await repo.Character.GetItemsAsync(chara.Id);

            if (!items.Any(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterHold || i.Status == CharacterItemStatus.CharacterPending)))
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            var info = CharacterItemInfoes.Get(itemType).GetOrError(ErrorCode.InvalidCommandParameter);

            if (!info.CanHandOver)
            {
                ErrorCode.InvalidCommandParameter.Throw();
            }

            if (info.IsResource && (info.DefaultResource < resourceSize || resourceSize == 0))
            {
                ErrorCode.NumberRangeError.Throw(new ErrorCode.RangeErrorParameter("resourceSize", resourceSize, 1, info.DefaultResource));
            }

            await repo.CharacterCommand.SetAsync(characterId, this.Type, gameDates, options);
        }
        public override async Task ExecuteAsync(MainRepository repo, Character character, IEnumerable <CharacterCommandParameter> options, CommandSystemData game)
        {
            var isRegularly = options.Any(p => p.Type == CharacterCommandParameterTypes.Regularly);

            var itemTypeOptional = options.FirstOrDefault(p => p.Type == 1);

            if (itemTypeOptional == null || itemTypeOptional.NumberValue == null)
            {
                await game.CharacterLogAsync("アイテム生産のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var itemType = (CharacterItemType)itemTypeOptional.NumberValue;

            var delays = await repo.DelayEffect.GetAllAsync();

            if (delays.Any(d => d.CharacterId == character.Id && d.Type == DelayEffectType.GenerateItem))
            {
                if (!isRegularly)
                {
                    await game.CharacterLogAsync($"アイテム生産しようとしましたが、複数のアイテムを同時に生産することはできません");
                }
                return;
            }

            var infoOptional = CharacterItemInfoes.Get(itemType);

            if (!infoOptional.HasData)
            {
                await game.CharacterLogAsync($"ID: {(short)itemType} のアイテムは存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var info = infoOptional.Data;

            var generateInfo = GenerateItemInfo.GetInfo(character, itemType);

            if (generateInfo == null)
            {
                await game.CharacterLogAsync("アイテム生産の情報が不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }

            var money = info.IsResource ? info.MoneyPerResource * info.DefaultResource / 16 : info.Money / 16;

            if (character.Money < money)
            {
                await game.CharacterLogAsync("アイテム生産しようとしましたが、金が足りません。<num>" + money + "</num> 必要です");

                return;
            }

            var townOptional = await repo.Town.GetByIdAsync(character.TownId);

            if (townOptional.HasData)
            {
                var country = await repo.Country.GetByIdAsync(character.CountryId);

                var town = townOptional.Data;
                if (town.CountryId != character.CountryId && country.HasData && !country.Data.HasOverthrown)
                {
                    await game.CharacterLogAsync($"<town>{town.Name}</town> でアイテム生産しようとしましたが、自国の都市ではありません");

                    return;
                }

                character.Money -= money;
                var delay = new DelayEffect
                {
                    CharacterId        = character.Id,
                    CountryId          = character.CountryId,
                    AppearGameDateTime = game.GameDateTime,
                    Type     = DelayEffectType.GenerateItem,
                    TypeData = (int)itemType,
                };
                await repo.DelayEffect.AddAsync(delay);

                await repo.SaveChangesAsync();

                await StatusStreaming.Default.SendCharacterAsync(ApiData.From(delay), character.Id);

                var finish = GameDateTime.FromInt(game.GameDateTime.ToInt() + generateInfo.Length);
                generateInfo.AddExAttribute(character);
                if (!isRegularly)
                {
                    character.Contribution += generateInfo.Contribution;
                    character.SkillPoint++;
                }
                await game.CharacterLogAsync($"<town>{town.Name}</town> で <num>{money}</num> を投し、{info.Name} の生産を開始しました。結果は <num>{finish.Year}</num> 年 <num>{finish.Month}</num> 月に来ます");
            }
            else
            {
                await game.CharacterLogAsync("ID:" + character.TownId + " の都市は存在しません。<emerge>管理者にお問い合わせください</emerge>");
            }
        }
Beispiel #6
0
        public static async Task SetCharacterAsync(MainRepository repo, CharacterItem item, Character chara)
        {
            if (item.Status == CharacterItemStatus.CharacterHold)
            {
                if (item.CharacterId == chara.Id)
                {
                    return;
                }

                var oldChara = await repo.Character.GetByIdAsync(item.CharacterId);

                if (oldChara.HasData)
                {
                    await ReleaseCharacterAsync(repo, item, oldChara.Data);
                }
            }

            var strong     = (short)item.GetSumOfValues(CharacterItemEffectType.Strong);
            var intellect  = (short)item.GetSumOfValues(CharacterItemEffectType.Intellect);
            var leadership = (short)item.GetSumOfValues(CharacterItemEffectType.Leadership);
            var popularity = (short)item.GetSumOfValues(CharacterItemEffectType.Popularity);

            chara.Strong     += strong;
            chara.Intellect  += intellect;
            chara.Leadership += leadership;
            chara.Popularity += popularity;

            item.Status      = CharacterItemStatus.CharacterHold;
            item.CharacterId = chara.Id;
            item.TownId      = 0;

            var infoOptional = CharacterItemInfoes.Get(item.Type);

            if (infoOptional.HasData)
            {
                var info = infoOptional.Data;
                if (info.IsResource)
                {
                    var characterItems = await repo.Character.GetItemsAsync(chara.Id);

                    var resource = characterItems.FirstOrDefault(i => i.Id != item.Id && i.Status == CharacterItemStatus.CharacterHold && i.Type == item.Type);
                    if (resource != null)
                    {
                        // 2つの資源アイテムを統合
                        resource.Resource += item.Resource;
                        item.Status        = CharacterItemStatus.CharacterSpent;
                        item.CharacterId   = 0;
                        item.Resource      = 0;
                        await StatusStreaming.Default.SendAllAsync(ApiData.From(resource));
                    }
                    else
                    {
                        // 今の資源アイテムがそのまま所持物となる
                        item.IsAvailable = true;
                    }
                }
            }

            await CharacterService.StreamCharacterAsync(repo, chara);

            await StatusStreaming.Default.SendAllAsync(ApiData.From(item));
        }
Beispiel #7
0
        public override async Task ExecuteAsync(MainRepository repo, Character character, IEnumerable <CharacterCommandParameter> options, CommandSystemData game)
        {
            var itemTypeOptional = options.FirstOrDefault(p => p.Type == 1).ToOptional();

            if (!itemTypeOptional.HasData)
            {
                await game.CharacterLogAsync("アイテム使用のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var itemType = (CharacterItemType)itemTypeOptional.Data.NumberValue;

            var infoOptional = CharacterItemInfoes.Get(itemType);

            if (!infoOptional.HasData)
            {
                await game.CharacterLogAsync($"ID: {(short)itemType} のアイテムは存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var info = infoOptional.Data;

            var charaItems = await repo.Character.GetItemsAsync(character.Id);

            var target = charaItems.FirstOrDefault(i => i.Type == itemType && i.Status == CharacterItemStatus.CharacterHold);

            if (target == null)
            {
                target = charaItems.FirstOrDefault(i => i.Type == itemType && i.Status == CharacterItemStatus.CharacterPending);
                var skills = await repo.Character.GetSkillsAsync(character.Id);

                if (target == null)
                {
                    await game.CharacterLogAsync($"アイテム {info.Name} を使用しようとしましたが、それは現在所持していません");

                    return;
                }
            }

            if (info.UsingEffects != null && info.UsingEffects.Any(ue => ue.Type == CharacterItemEffectType.AddSubBuildingExtraSize))
            {
                var townOptional = await repo.Town.GetByIdAsync(character.TownId);

                if (townOptional.HasData)
                {
                    var town = townOptional.Data;
                    if (town.CountryId != character.CountryId)
                    {
                        await game.CharacterLogAsync($"アイテム {info.Name} は、自国の都市でしか使用できません");

                        return;
                    }
                    if (town.TownSubBuildingExtraSpace + info.UsingEffects.Where(ue => ue.Type == CharacterItemEffectType.AddSubBuildingExtraSize).Sum(ue => ue.Value) > 4)
                    {
                        await game.CharacterLogAsync($"アイテム {info.Name} を使おうとしましたが、<town>{town.Name}</town> の追加の敷地を <num>4</num> より多くすることはできません");

                        return;
                    }
                }
            }

            var log = await ItemService.SpendCharacterAsync(repo, target, character);

            await game.CharacterLogAsync($"アイテム {info.Name} を使用しました。効果: {log}");
        }
Beispiel #8
0
        public override async Task ExecuteAsync(MainRepository repo, Character character, IEnumerable <CharacterCommandParameter> options, CommandSystemData game)
        {
            var townOptional = await repo.Town.GetByIdAsync(character.TownId);

            var itemTypeOptional     = options.FirstOrDefault(p => p.Type == 1).ToOptional();
            var resourceSizeOptional = options.FirstOrDefault(p => p.Type == 3).ToOptional();

            if (!townOptional.HasData)
            {
                await game.CharacterLogAsync("ID:" + character.TownId + " の都市は存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var town = townOptional.Data;

            if (!itemTypeOptional.HasData)
            {
                await game.CharacterLogAsync("アイテム売却のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var itemType = (CharacterItemType)itemTypeOptional.Data.NumberValue;

            var infoOptional = CharacterItemInfoes.Get(itemType);

            if (!infoOptional.HasData)
            {
                await game.CharacterLogAsync($"ID: {(short)itemType} のアイテムは存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var info = infoOptional.Data;

            if (info.IsResource)
            {
                if (resourceSizeOptional.Data?.NumberValue == null)
                {
                    await game.CharacterLogAsync("アイテム(資源)売却のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                    return;
                }
            }

            CharacterItem target;
            var           resourceSize = resourceSizeOptional.Data?.NumberValue ?? 0;

            if (!info.IsResource)
            {
                var charaItems = await repo.Character.GetItemsAsync(character.Id);

                target = charaItems.FirstOrDefault(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterHold || i.Status == CharacterItemStatus.CharacterPending));
            }
            else
            {
                var charaItems = await repo.Character.GetItemsAsync(character.Id);

                var targets = charaItems.Where(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterHold || i.Status == CharacterItemStatus.CharacterPending));
                if (!targets.Any())
                {
                    target = null;
                }
                else
                {
                    var allSize = targets.Sum(i => i.Resource);
                    if (allSize < resourceSize)
                    {
                        resourceSize = allSize;
                    }

                    if (targets.First().Resource > resourceSize)
                    {
                        target = await ItemService.DivideResourceAndSaveAsync(repo, targets.First(), resourceSize);
                    }
                    else if (targets.First().Resource == resourceSize)
                    {
                        target = targets.First();
                    }
                    else if (targets.Count() >= 2)
                    {
                        target = targets.First();
                        var currentSize = target.Resource;
                        foreach (var t in targets.Skip(1))
                        {
                            currentSize += t.Resource;
                            var size = currentSize <= resourceSize ? 0 : currentSize - resourceSize;
                            target.Resource += t.Resource - size;
                            t.Resource       = size;
                            if (t.Resource == 0)
                            {
                                t.Status      = CharacterItemStatus.CharacterSpent;
                                t.CharacterId = 0;
                            }
                            await StatusStreaming.Default.SendAllAsync(ApiData.From(t));

                            if (currentSize >= resourceSize)
                            {
                                break;
                            }
                        }
                        await StatusStreaming.Default.SendAllAsync(ApiData.From(target));
                    }
                    else
                    {
                        target       = targets.First();
                        resourceSize = target.Resource;
                    }
                }
            }
            if (target == null)
            {
                await game.CharacterLogAsync($"アイテム {info.Name} を売却しようとしましたが、それは現在所持していません");

                return;
            }
            var itemMoney = info.GetMoney(target);

            character.Money += itemMoney / 2;
            await ItemService.ReleaseCharacterAsync(repo, target, character);

            await game.CharacterLogAsync($"<town>{town.Name}</town> にアイテム {info.Name} を売却しました。金 <num>+{itemMoney / 2}</num>");
        }
Beispiel #9
0
        public override async Task ExecuteAsync(MainRepository repo, Character character, IEnumerable <CharacterCommandParameter> options, CommandSystemData game)
        {
            var itemTypeOptional     = options.FirstOrDefault(p => p.Type == 1).ToOptional();
            var townIdOptional       = options.FirstOrDefault(p => p.Type == 2).ToOptional();
            var resourceSizeOptional = options.FirstOrDefault(p => p.Type == 4).ToOptional();

            if (!itemTypeOptional.HasData || !townIdOptional.HasData)
            {
                await game.CharacterLogAsync("アイテム購入のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var itemType = (CharacterItemType)itemTypeOptional.Data.NumberValue;

            var townOptional = await repo.Town.GetByIdAsync((uint)townIdOptional.Data.NumberValue);

            if (!townOptional.HasData)
            {
                await game.CharacterLogAsync("ID:" + character.TownId + " の都市は存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var town = townOptional.Data;

            var infoOptional = CharacterItemInfoes.Get(itemType);

            if (!infoOptional.HasData)
            {
                await game.CharacterLogAsync($"ID: {(short)itemType} のアイテムは存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var info = infoOptional.Data;

            if (info.IsResource)
            {
                if (resourceSizeOptional.Data?.NumberValue == null)
                {
                    await game.CharacterLogAsync("アイテム(資源)購入のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                    return;
                }
            }

            var items = await repo.Character.GetItemsAsync(character.Id);

            var itemsMax = CharacterService.GetItemMax(await repo.Character.GetSkillsAsync(character.Id));

            if ((!info.IsResource || info.IsResourceItem) && CharacterService.CountLimitedItems(items) >= itemsMax)
            {
                await game.CharacterLogAsync($"アイテム購入しようとしましたが、アイテム所持数が上限 <num>{itemsMax}</num> に達しています");

                return;
            }

            List <Func <Task> > onSucceeds = new List <Func <Task> >();

            CharacterItem target;
            var           resourceSize = resourceSizeOptional.Data?.NumberValue ?? 0;

            if (!info.IsResource)
            {
                var allItems = await repo.CharacterItem.GetAllAsync();

                target = allItems.FirstOrDefault(i => i.Status == CharacterItemStatus.TownOnSale && i.TownId == townIdOptional.Data.NumberValue && i.Type == itemType);
            }
            else
            {
                var allItems = await repo.CharacterItem.GetAllAsync();

                var targets = allItems.Where(i => i.Status == CharacterItemStatus.TownOnSale && i.TownId == townIdOptional.Data.NumberValue && i.Type == itemType);
                if (!targets.Any())
                {
                    target = null;
                }
                else
                {
                    var allSize = targets.Sum(i => i.Resource);
                    if (allSize < resourceSize)
                    {
                        resourceSize = allSize;
                    }

                    if (targets.First().Resource > resourceSize)
                    {
                        target = await ItemService.DivideResourceAndSaveAsync(repo, targets.First(), resourceSize);
                    }
                    else if (targets.First().Resource == resourceSize)
                    {
                        target = targets.First();
                    }
                    else if (targets.Count() >= 2)
                    {
                        target = targets.First();
                        onSucceeds.Add(async() =>
                        {
                            var currentSize = target.Resource;
                            foreach (var t in targets.Skip(1))
                            {
                                currentSize     += t.Resource;
                                var size         = currentSize <= resourceSize ? 0 : currentSize - resourceSize;
                                target.Resource += t.Resource - size;
                                t.Resource       = size;
                                if (t.Resource == 0)
                                {
                                    t.Status = CharacterItemStatus.CharacterSpent;
                                    t.TownId = 0;
                                }
                                await StatusStreaming.Default.SendAllAsync(ApiData.From(t));

                                if (currentSize >= resourceSize)
                                {
                                    break;
                                }
                            }
                            await StatusStreaming.Default.SendAllAsync(ApiData.From(target));
                        });
                    }
                    else
                    {
                        target       = targets.First();
                        resourceSize = target.Resource;
                    }
                }
            }
            if (target == null)
            {
                await game.CharacterLogAsync($"アイテムを購入しようとしましたが、<town>{town.Name}</town> の {info.Name} はすでに売り切れています");

                return;
            }

            var skills = await repo.Character.GetSkillsAsync(character.Id);

            var discountRate = (1 - skills.GetSumOfValues(CharacterSkillEffectType.ItemDiscountPercentage) / 100.0f);
            var needMoney    = !info.IsResource ?
                               (int)(info.Money * discountRate) :
                               (int)(info.MoneyPerResource * discountRate * resourceSize);

            if (needMoney > character.Money)
            {
                await game.CharacterLogAsync("アイテム購入の金が足りません");

                return;
            }

            foreach (var run in onSucceeds)
            {
                await run();
            }

            character.Money -= needMoney;
            await ItemService.SetCharacterAsync(repo, target, character);

            await game.CharacterLogAsync($"<num>{needMoney}</num> を投じて、<town>{town.Name}</town> のアイテム {info.Name} を購入しました");
        }
Beispiel #10
0
        public static async Task <bool> ResultAsync(MainRepository repo, SystemData system, DelayEffect delay, Func <uint, string, Task> logAsync)
        {
            if (delay.IntAppearGameDateTime + 36 <= system.IntGameDateTime)
            {
                var chara = await repo.Character.GetByIdAsync(delay.CharacterId);

                var town = await repo.Town.GetByIdAsync(delay.TownId);

                if (chara.HasData && town.HasData)
                {
                    var results = new List <string>();
                    var skills  = await repo.Character.GetSkillsAsync(chara.Data.Id);

                    var currentItems = await repo.Character.GetItemsAsync(chara.Data.Id);

                    var items = (await repo.Town.GetItemsAsync(chara.Data.TownId)).Where(i => i.Status == CharacterItemStatus.TownHidden);
                    if (RandomService.Next(0, 10) < items.Count())
                    {
                        var item = RandomService.Next(items);
                        var info = CharacterItemInfoes.Get(item.Type);
                        if (info.HasData && (info.Data.DiscoverFroms == null || info.Data.DiscoverFroms.Contains(chara.Data.From)))
                        {
                            await ItemService.SetCharacterPendingAsync(repo, item, chara.Data);

                            results.Add($"アイテム {info.Data.Name}");
                        }
                    }

                    var money = RandomService.Next(chara.Data.Intellect * 32, Math.Max(chara.Data.Intellect * 48, system.GameDateTime.Year * 650 + 10000));
                    chara.Data.Money += money;
                    results.Add($"金 <num>{money}</num>");

                    var country = await repo.Country.GetByIdAsync(chara.Data.CountryId);

                    if (country.HasData)
                    {
                        var name   = string.Empty;
                        var add    = RandomService.Next(20, 60);
                        var target = RandomService.Next(0, 5);
                        if (target == 0)
                        {
                            name = "農業";
                            town.Data.Agriculture = Math.Min(town.Data.AgricultureMax, town.Data.Agriculture + add);
                        }
                        else if (target == 1)
                        {
                            name = "商業";
                            town.Data.Commercial = Math.Min(town.Data.CommercialMax, town.Data.Commercial + add);
                        }
                        else if (target == 2)
                        {
                            name = "技術";
                            town.Data.Technology = Math.Min(town.Data.TechnologyMax, town.Data.Technology + add);
                        }
                        else if (target == 3)
                        {
                            name           = "城壁";
                            town.Data.Wall = Math.Min(town.Data.WallMax, town.Data.Wall + add);
                        }
                        else if (target == 4)
                        {
                            name = "都市施設";
                            town.Data.TownBuildingValue = Math.Min(Config.TownBuildingMax, town.Data.TownBuildingValue + add);
                        }

                        await logAsync(chara.Data.Id, $"<town>{town.Data.Name}</town> に投資し、{name} の開発に <num>+{add}</num> 貢献し、{string.Join("と", results)}を得ました");
                    }
                    else
                    {
                        await logAsync(chara.Data.Id, $"<town>{town.Data.Name}</town> に投資し、{string.Join("と", results)}を得ました");
                    }

                    return(true);
                }
            }

            return(false);
        }
        public override async Task ExecuteAsync(MainRepository repo, Character character, IEnumerable <CharacterCommandParameter> options, CommandSystemData game)
        {
            var itemTypeOptional     = options.FirstOrDefault(p => p.Type == 1).ToOptional();
            var targetIdOptional     = options.FirstOrDefault(p => p.Type == 2).ToOptional();
            var resourceSizeOptional = options.FirstOrDefault(p => p.Type == 4).ToOptional();

            if (!itemTypeOptional.HasData || !targetIdOptional.HasData)
            {
                await game.CharacterLogAsync("アイテム売却のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var itemType = (CharacterItemType)itemTypeOptional.Data.NumberValue;

            var targetOptional = await repo.Character.GetByIdAsync((uint)targetIdOptional.Data.NumberValue);

            if (!targetOptional.HasData)
            {
                await game.CharacterLogAsync("ID:" + character.TownId + " の武将は存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            if (targetOptional.Data.HasRemoved)
            {
                await game.CharacterLogAsync($"<character>{targetOptional.Data.Name}</character> にアイテムを譲渡しようとしましたが、その武将はすでに削除されています");

                return;
            }
            if (character.AiType == CharacterAiType.Human && targetOptional.Data.AiType != CharacterAiType.Human && targetOptional.Data.AiType != CharacterAiType.Administrator)
            {
                await game.CharacterLogAsync($"<character>{targetOptional.Data.Name}</character> にアイテムを譲渡しようとしましたが、その武将は人間ではありません");

                return;
            }
            var target = targetOptional.Data;

            var infoOptional = CharacterItemInfoes.Get(itemType);

            if (!infoOptional.HasData)
            {
                await game.CharacterLogAsync($"ID: {(short)itemType} のアイテムは存在しません。<emerge>管理者にお問い合わせください</emerge>");

                return;
            }
            var info = infoOptional.Data;

            if (info.IsResource)
            {
                if (resourceSizeOptional.Data?.NumberValue == null)
                {
                    await game.CharacterLogAsync("アイテム(資源)譲渡のパラメータが不正です。<emerge>管理者にお問い合わせください</emerge>");

                    return;
                }
            }

            var charaItems = await repo.Character.GetItemsAsync(character.Id);

            var item = charaItems.FirstOrDefault(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterHold));

            if (item == null)
            {
                item = charaItems.FirstOrDefault(i => i.Type == itemType && (i.Status == CharacterItemStatus.CharacterPending));
            }
            if (item == null)
            {
                await game.CharacterLogAsync($"アイテム {info.Name} を譲渡しようとしましたが、それは現在所持していません");

                return;
            }

            var resourceSize = resourceSizeOptional.Data?.NumberValue ?? 0;

            if (info.IsResource)
            {
                // 譲渡や購入などで、資源がすべて1つのアイテムデータとしてまとめられているのが前提
                // 同じ資源アイテムを2つ以上持ってた場合は想定しない
                if (item.Resource < resourceSize)
                {
                    resourceSize = item.Resource;
                }
            }

            character.Contribution += 15;
            character.SkillPoint++;

            if (!info.IsResource)
            {
                await ItemService.ReleaseCharacterAsync(repo, item, character);

                await ItemService.SetCharacterPendingAsync(repo, item, target);

                await game.CharacterLogAsync($"<character>{target.Name}</character> にアイテム {info.Name} を譲渡しました");

                await game.CharacterLogByIdAsync(target.Id, $"<character>{character.Name}</character> からアイテム {info.Name} を受け取りました");
            }
            else
            {
                if (item.Resource > resourceSize)
                {
                    // 資源の分解
                    var newItem = await ItemService.DivideResourceAndSaveAsync(repo, item, resourceSize);

                    await ItemService.ReleaseCharacterAsync(repo, newItem, character);

                    await ItemService.SetCharacterPendingAsync(repo, newItem, target);
                }
                else
                {
                    await ItemService.ReleaseCharacterAsync(repo, item, character);

                    await ItemService.SetCharacterPendingAsync(repo, item, target);
                }
                await game.CharacterLogAsync($"<character>{target.Name}</character> に資源 {info.Name} を <num>{resourceSize}</num> 譲渡しました");

                await game.CharacterLogByIdAsync(target.Id, $"<character>{character.Name}</character> から資源 {info.Name} を <num>{resourceSize}</num> 受け取りました");
            }
        }