1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-24 22:14:36 +02:00

Merge pull request #1771 from IvanSavenko/beta_fixes

Fixes for issues discovered in beta
This commit is contained in:
Ivan Savenko 2023-03-26 01:36:38 +02:00 committed by GitHub
commit 6a3f4fd73d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 415 additions and 72 deletions

View File

@ -0,0 +1,289 @@
{
"vcmi.adventureMap.monsterThreat.title" : "\n\n 威胁等级: ",
"vcmi.adventureMap.monsterThreat.levels.0" : "极低",
"vcmi.adventureMap.monsterThreat.levels.1" : "很低",
"vcmi.adventureMap.monsterThreat.levels.2" : "低",
"vcmi.adventureMap.monsterThreat.levels.3" : "较低",
"vcmi.adventureMap.monsterThreat.levels.4" : "中等",
"vcmi.adventureMap.monsterThreat.levels.5" : "较高",
"vcmi.adventureMap.monsterThreat.levels.6" : "高",
"vcmi.adventureMap.monsterThreat.levels.7" : "很高",
"vcmi.adventureMap.monsterThreat.levels.8" : "挑战性的",
"vcmi.adventureMap.monsterThreat.levels.9" : "压倒性的",
"vcmi.adventureMap.monsterThreat.levels.10" : "致命的",
"vcmi.adventureMap.monsterThreat.levels.11" : "无法取胜的",
"vcmi.adventureMap.confirmRestartGame" : "你想要重新开始游戏吗?",
"vcmi.adventureMap.noTownWithMarket" : "没有足够的市场。",
"vcmi.adventureMap.noTownWithTavern" : "没有酒馆可供查看。",
"vcmi.adventureMap.spellUnknownProblem" : "无此魔法的信息。",
"vcmi.adventureMap.playerAttacked" : "玩家遭受攻击: %s",
"vcmi.adventureMap.moveCostDetails" : "移动点数 - 花费: %TURNS 轮 + %POINTS 点移动力, 剩余移动力: %REMAINING",
"vcmi.adventureMap.moveCostDetailsNoTurns" : "移动点数 - 花费: %POINTS 点移动力, 剩余移动力: %REMAINING",
"vcmi.server.errors.existingProcess" : "另一个VCMI进程在运行,请结束当前进程。",
"vcmi.server.errors.modsIncompatibility" : "需要加载mod:",
"vcmi.server.confirmReconnect" : "连接到上次吗?",
"vcmi.settingsMainWindow.generalTab.hover" : "常规",
"vcmi.settingsMainWindow.generalTab.help" : "切换到“系统选项”选项卡 - 这些设置与常规游戏客户端行为相关",
"vcmi.settingsMainWindow.battleTab.hover" : "战斗",
"vcmi.settingsMainWindow.battleTab.help" : "切换到“战斗选项”选项卡 - 这些设置允许配置战斗界面和相关内容",
"vcmi.settingsMainWindow.adventureTab.hover" : "冒险地图",
"vcmi.settingsMainWindow.adventureTab.help" : "切换到“冒险地图”选项卡 - 冒险地图允许你移动英雄",
"vcmi.settingsMainWindow.otherTab.hover" : "其他设置",
"vcmi.settingsMainWindow.otherTab.help" : "切换到“其他设置”选项卡 - 由于各种原因,这些选项不适合其他类别",
"vcmi.systemOptions.fullscreenButton.hover" : "全屏",
"vcmi.systemOptions.fullscreenButton.help" : "{全屏n}\n\n 当你选择全屏时,VCMI将会全屏运行,否则只会运行在指定框内",
"vcmi.systemOptions.resolutionButton.hover" : "分辨率",
"vcmi.systemOptions.resolutionButton.help" : "{选择分辨率}\n\n 改变游戏的分辨率,达到更加清晰的效果。需要重新启动才能完成更改。",
"vcmi.systemOptions.resolutionMenu.hover" : "选择分辨率",
"vcmi.systemOptions.resolutionMenu.help" : "选择游戏的分辨率。",
"vcmi.systemOptions.fullscreenFailed" : "{全屏}\n\n 选择切换到全屏失败!当前分辨率不支持全屏!",
"vcmi.systemOptions.framerateButton.hover" : "显示传输帧数",
"vcmi.systemOptions.framerateButton.help" : "{显示传输帧数}\n\n 打开/关闭在游戏窗口角落的传输帧数计数器。",
"vcmi.adventureOptions.numericQuantities.hover" : "生物数量显示",
"vcmi.adventureOptions.numericQuantities.help" : "{生物数量显示}\n\n 以数字 A-B 格式显示不准确的敌方生物数量。",
"vcmi.adventureOptions.forceMovementInfo.hover" : "在状态栏中显示移动力",
"vcmi.adventureOptions.forceMovementInfo.help" : "{在状态栏中显示移动力}\n\n 不需要按ALT就可以显示移动力。",
"vcmi.adventureOptions.showGrid.hover" : "显示六角网格",
"vcmi.adventureOptions.showGrid.help" : "{显示六角网格}\n\n 在战场上显示六角网格。",
"vcmi.adventureOptions.mapScrollSpeed4.hover": "4",
"vcmi.adventureOptions.mapScrollSpeed4.help": "设置动画速度为超快",
"vcmi.adventureOptions.mapScrollSpeed5.hover": "5",
"vcmi.adventureOptions.mapScrollSpeed5.help": "设置动画速度为极速",
"vcmi.battleOptions.showQueue.hover": "显示移动次序",
"vcmi.battleOptions.showQueue.help": "{显示移动次序}\n\n 显示当前生物的移动次序。",
"vcmi.battleOptions.queueSizeLabel.hover": "次序条尺寸 (设置后下一场战斗生效)",
"vcmi.battleOptions.queueSizeAutoButton.hover": "自动设置尺寸",
"vcmi.battleOptions.queueSizeAutoButton.help": "根据游戏分辨率设置尺寸 (像素小于700为小尺寸,根据实际调整)",
"vcmi.battleOptions.queueSizeSmallButton.hover": "小尺寸",
"vcmi.battleOptions.queueSizeSmallButton.help": "设置次序条为小尺寸",
"vcmi.battleOptions.queueSizeBigButton.hover": "大尺寸",
"vcmi.battleOptions.queueSizeBigButton.help": "设置次寻条为大尺寸(不能在像素小于700时生效)",
"vcmi.battleOptions.animationsSpeed4.hover": "4",
"vcmi.battleOptions.animationsSpeed4.help": "设置动画速度为超快",
"vcmi.battleOptions.animationsSpeed5.hover": "5",
"vcmi.battleOptions.animationsSpeed5.help": "设置动画速度为极速",
"vcmi.battleOptions.animationsSpeed6.hover": "6",
"vcmi.battleOptions.animationsSpeed6.help": "设置动画速度为最快",
"vcmi.battleOptions.skipBattleIntroMusic.hover": "跳过开场音乐",
"vcmi.battleOptions.skipBattleIntroMusic.help": "{跳过开场音乐}\n\n 战斗开始时跳过开场音乐,直接按Esc也可以跳过。",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.hover" : "显示所有可以招募的城镇生物",
"vcmi.otherOptions.availableCreaturesAsDwellingLabel.help" : "{显示所有可以招募的城镇生物}\n\n 显示当前所有可供招募的城镇生物 (左下角)。",
"vcmi.otherOptions.compactTownCreatureInfo.hover": "缩小城镇生物信息",
"vcmi.otherOptions.compactTownCreatureInfo.help": "{缩小城镇生物信息}\n\n 将城镇生物信息最小化.",
"vcmi.townHall.missingBase" : "你必须先建造%s ",
"vcmi.townHall.noCreaturesToRecruit" : "没有可供雇佣的生物。",
"vcmi.townHall.greetingManaVortex" : "当你接近%s时,你的身体充满了新的能量。这使你的魔法值加倍。",
"vcmi.townHall.greetingKnowledge" : "你学习了%s上的图形,并深入了解各种魔法的运作,这使你的知识点数+1。",
"vcmi.townHall.greetingSpellPower" : "%s教你新的方法来集中你的魔法力量,这使你的力量点数+1。",
"vcmi.townHall.greetingExperience" : "访问%s给你提供了更好的学习方法。这使你的经验值+1000。",
"vcmi.townHall.greetingAttack" : "在%s参观后给你提供了更好的战斗技巧,这使你的攻击点数+1。",
"vcmi.townHall.greetingDefence" : "在%s中度过一段时间后,经验丰富的勇士会教你额外的防御技能,这使你的防御点数+1。",
"vcmi.townHall.hasNotProduced" : "本周%s并没有产生什么资源。",
"vcmi.townHall.hasProduced" : "本周%s产生了%d个%s。",
"vcmi.townHall.greetingCustomBonus" : "参观%s后,你的技巧有了提升。这使你受益匪浅。并且使你+%d %s%s",
"vcmi.townHall.greetingCustomUntil" : "直到下一场战斗。",
"vcmi.townHall.greetingInTownMagicWell" : "%s使你的魔法值恢复到最大值。",
"vcmi.logicalExpressions.anyOf" : "以下任何前提:",
"vcmi.logicalExpressions.allOf" : "以下所有前提:",
"vcmi.logicalExpressions.noneOf" : "无前提:",
"vcmi.heroWindow.openCommander.hover" : "开启指挥官界面",
"vcmi.heroWindow.openCommander.help" : "开启英雄的指挥官界面",
"vcmi.commanderWindow.artifactMessage" : "你要把这个宝物还给英雄吗?",
"vcmi.creatureWindow.showBonuses.hover" : "属性界面",
"vcmi.creatureWindow.showBonuses.help" : "显示指挥官的所有增强属性",
"vcmi.creatureWindow.showSkills.hover" : "技能页面",
"vcmi.creatureWindow.showSkills.help" : "显示指挥官的所有技能",
"vcmi.creatureWindow.returnArtifact.hover" : "交换宝物",
"vcmi.creatureWindow.returnArtifact.help" : "将宝物还到英雄的背包里",
"vcmi.questLog.hideComplete.hover" : "隐藏完成任务",
"vcmi.questLog.hideComplete.help" : "隐藏所有完成的任务",
"vcmi.randomMapTab.widgets.defaultTemplate" : "默认",
"vcmi.randomMapTab.widgets.templateLabel" : "格式",
"vcmi.randomMapTab.widgets.teamAlignmentsButton" : "设置...",
"vcmi.randomMapTab.widgets.teamAlignmentsLabel" : "同盟关系",
// few strings from WoG used by vcmi
"vcmi.stackExperience.description" : "» 经 验 获 得 明 细 «\n\n生物类型 ................... : %s\n经验等级 ................. : %s (%i)\n经验点数 ............... : %i\n下一个等级所需经验 .. : %i\n每次战斗最大获得经验 ... : %i%% (%i)\n获得经验的生物数量 .... : %i\n最大招募数量\n不会丢失经验升级 .... : %i\n经验倍数 ........... : %.2f\n升级倍数 .............. : %.2f\n10级后经验值 ........ : %i\n最大招募数量下\n 升级到10级所需经验数量: %i",
"vcmi.stackExperience.rank.1" : "新兵 1级",
"vcmi.stackExperience.rank.2" : "列兵 2级",
"vcmi.stackExperience.rank.3" : "下士 3级",
"vcmi.stackExperience.rank.4" : "中士 4级",
"vcmi.stackExperience.rank.5" : "上士 5级",
"vcmi.stackExperience.rank.6" : "少尉 6级",
"vcmi.stackExperience.rank.7" : "中尉 7级",
"vcmi.stackExperience.rank.8" : "上尉 8级",
"vcmi.stackExperience.rank.9" : "少校 9级",
"vcmi.stackExperience.rank.10" : "中校 10级",
"vcmi.stackExperience.rank.11" : "上校 11级",
"core.bonus.ADDITIONAL_ATTACK.name": "双击",
"core.bonus.ADDITIONAL_ATTACK.description": "可以攻击两次",
"core.bonus.ADDITIONAL_RETALIATION.name": "额外反击",
"core.bonus.ADDITIONAL_RETALIATION.description": "可以额外反击 ${val} 次",
"core.bonus.AIR_IMMUNITY.name": "气系免疫",
"core.bonus.AIR_IMMUNITY.description": "免疫所有气系魔法",
"core.bonus.ATTACKS_ALL_ADJACENT.name": "环击",
"core.bonus.ATTACKS_ALL_ADJACENT.description": "攻击所有相邻部队",
"core.bonus.BLOCKS_RETALIATION.name": "无反击",
"core.bonus.BLOCKS_RETALIATION.description": "敌人无法反击",
"core.bonus.BLOCKS_RANGED_RETALIATION.name": "远程无反击",
"core.bonus.BLOCKS_RANGED_RETALIATION.description": "敌人无法对射击进行反击",
"core.bonus.CATAPULT.name": "攻城",
"core.bonus.CATAPULT.description": "可以攻击城墙",
"core.bonus.CATAPULT_EXTRA_SHOTS.name": "额外攻击城墙",
"core.bonus.CATAPULT_EXTRA_SHOTS.description": "可以额外攻击城墙 ${val} 次",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.name": "施法消耗 - (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ALLY.description": "减少英雄的施法消耗",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.name": "对方施法消耗 + (${val})",
"core.bonus.CHANGES_SPELL_COST_FOR_ENEMY.description": "增加对方施法消耗",
"core.bonus.CHARGE_IMMUNITY.name": "I免疫冲锋",
"core.bonus.CHARGE_IMMUNITY.description": "对冲锋特技的额外伤害免疫",
"core.bonus.DARKNESS.name": "黑暗天幕",
"core.bonus.DARKNESS.description": "增加 ${val} 半径黑幕",
"core.bonus.DEATH_STARE.name": "死亡凝视 (${val}%)",
"core.bonus.DEATH_STARE.description": "${val}% 几率直接杀死生物",
"core.bonus.DEFENSIVE_STANCE.name": "防御奖励",
"core.bonus.DEFENSIVE_STANCE.description": "当选择防御时+${val} 防御力",
"core.bonus.DESTRUCTION.name": "毁灭",
"core.bonus.DESTRUCTION.description": "有${val}% 杀死额外数量的部队",
"core.bonus.DOUBLE_DAMAGE_CHANCE.name": "致命一击",
"core.bonus.DOUBLE_DAMAGE_CHANCE.description": "${val}% 几率造成双倍伤害",
"core.bonus.DRAGON_NATURE.name": "龙",
"core.bonus.DRAGON_NATURE.description": "生物属于龙类",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.name": "魔法伤害免疫",
"core.bonus.DIRECT_DAMAGE_IMMUNITY.description": "对魔法伤害免疫",
"core.bonus.EARTH_IMMUNITY.name": "土系免疫",
"core.bonus.EARTH_IMMUNITY.description": "免疫所有土系魔法",
"core.bonus.ENCHANTER.name": "施法者",
"core.bonus.ENCHANTER.description": "每回合群体施放 ${subtype.spell} ",
"core.bonus.ENCHANTED.name": "魔法护身",
"core.bonus.ENCHANTED.description": "自身被 ${subtype.spell} 魔法影响",
"core.bonus.ENEMY_DEFENCE_REDUCTION.name": "忽略防御 (${val}%)",
"core.bonus.ENEMY_DEFENCE_REDUCTION.description": "攻击时忽略对方部分防御力",
"core.bonus.FIRE_IMMUNITY.name": "火系免疫",
"core.bonus.FIRE_IMMUNITY.description": "免疫所有火系魔法",
"core.bonus.FIRE_SHIELD.name": "烈火神盾 (${val}%)",
"core.bonus.FIRE_SHIELD.description": "拥有烈火神盾护身",
"core.bonus.FIRST_STRIKE.name": "抢先攻击",
"core.bonus.FIRST_STRIKE.description": "在被反击前做出攻击",
"core.bonus.FEAR.name": "恐惧",
"core.bonus.FEAR.description": "引起恐惧",
"core.bonus.FEARLESS.name": "无惧",
"core.bonus.FEARLESS.description": "免疫恐惧",
"core.bonus.FLYING.name": "飞行兵种",
"core.bonus.FLYING.description": "生物可以飞行",
"core.bonus.FREE_SHOOTING.name": "近身射击",
"core.bonus.FREE_SHOOTING.description": "靠近敌方也能射击",
"core.bonus.FULL_HP_REGENERATION.name": "重生",
"core.bonus.FULL_HP_REGENERATION.description": "可以自动恢复所有生命值",
"core.bonus.GARGOYLE.name": "石像鬼属性",
"core.bonus.GARGOYLE.description": "不能被复活或治疗",
"core.bonus.GENERAL_DAMAGE_REDUCTION.name": "减少伤害 (${val}%)",
"core.bonus.GENERAL_DAMAGE_REDUCTION.description": "受攻击时减少受到的伤害",
"core.bonus.HATE.name": "${subtype.creature}的死敌",
"core.bonus.HATE.description": "对该部队造成 ${val}% 的额外伤害",
"core.bonus.HEALER.name": "治疗",
"core.bonus.HEALER.description": "可以治疗友军单位",
"core.bonus.HP_REGENERATION.name": "重生",
"core.bonus.HP_REGENERATION.description": "每回合恢复 ${val} 点生命值",
"core.bonus.JOUSTING.name": "冲锋",
"core.bonus.JOUSTING.description": "每格行动增加+5%伤害",
"core.bonus.KING1.name": "一般顶级怪物",
"core.bonus.KING1.description": "被初级屠戮成性影响",
"core.bonus.KING2.name": "智慧顶级怪物",
"core.bonus.KING2.description": "被中级屠戮成性影响",
"core.bonus.KING3.name": "精神顶级怪物",
"core.bonus.KING3.description":"被高级屠戮成性影响",
"core.bonus.LEVEL_SPELL_IMMUNITY.name": "免疫 1-${val} 级魔法",
"core.bonus.LEVEL_SPELL_IMMUNITY.description": "免疫等级为 1-${val} 级的所有魔法",
"core.bonus.LIMITED_SHOOTING_RANGE.name" : "半程射击",
"core.bonus.LIMITED_SHOOTING_RANGE.description" : "超过 ${val} 格不能射击",
"core.bonus.LIFE_DRAIN.name": "吸取生命 (${val}%)",
"core.bonus.LIFE_DRAIN.description": "吸取 ${val}% 伤害回复自身",
"core.bonus.MANA_CHANNELING.name": "偷取魔法 ${val}%",
"core.bonus.MANA_CHANNELING.description": "偷取部分敌人施法消耗",
"core.bonus.MANA_DRAIN.name": "吸取魔力",
"core.bonus.MANA_DRAIN.description": "每回合吸取 ${val} 魔法值",
"core.bonus.MAGIC_MIRROR.name": "带有魔法神镜 (${val}%)",
"core.bonus.MAGIC_MIRROR.description": "${val}% 几率反射魔法",
"core.bonus.MAGIC_RESISTANCE.name": "(${MR}%) 魔法抵抗",
"core.bonus.MAGIC_RESISTANCE.description": "${MR}% 几率抵抗敌人的魔法",
"core.bonus.MIND_IMMUNITY.name": "免疫心智",
"core.bonus.MIND_IMMUNITY.description": "不受心智魔法的影响",
"core.bonus.NO_DISTANCE_PENALTY.name": "无障碍射击",
"core.bonus.NO_DISTANCE_PENALTY.description": "射击不受距离影响",
"core.bonus.NO_MELEE_PENALTY.name": "无近战惩罚",
"core.bonus.NO_MELEE_PENALTY.description": "近战伤害不减",
"core.bonus.NO_MORALE.name": "无士气",
"core.bonus.NO_MORALE.description": "生物不受士气影响",
"core.bonus.NO_WALL_PENALTY.name": "无城墙影响",
"core.bonus.NO_WALL_PENALTY.description": "射击不受城墙的影响",
"core.bonus.NON_LIVING.name": "无生命",
"core.bonus.NON_LIVING.description": "不受只对生命实体生物有效的魔法",
"core.bonus.RANDOM_SPELLCASTER.name": "随机施法",
"core.bonus.RANDOM_SPELLCASTER.description": "随机施放增益魔法",
"core.bonus.RANGED_RETALIATION.name": "远程反击",
"core.bonus.RANGED_RETALIATION.description": "可以对远程攻击进行反击",
"core.bonus.RECEPTIVE.name": "接受有益魔法",
"core.bonus.RECEPTIVE.description": "不会免疫有益的魔法",
"core.bonus.REBIRTH.name": "复生 (${val}%)",
"core.bonus.REBIRTH.description": "{val}% 数量死亡后会复活",
"core.bonus.RETURN_AFTER_STRIKE.name": "攻击并返回",
"core.bonus.RETURN_AFTER_STRIKE.description": "攻击后回到初始位置",
"core.bonus.SELF_LUCK.name": "永久幸运",
"core.bonus.SELF_LUCK.description": "永久拥有幸运值",
"core.bonus.SELF_MORALE.name": "士气高涨",
"core.bonus.SELF_MORALE.description": "永久拥有高昂的士气",
"core.bonus.SHOOTER.name": "射手",
"core.bonus.SHOOTER.description": "生物可以设计",
"core.bonus.SHOOTS_ALL_ADJACENT.name": "范围远程攻击",
"core.bonus.SHOOTS_ALL_ADJACENT.description": "远程攻击可伤害范围内的多个目标",
"core.bonus.SOUL_STEAL.name": "杀死敌人复生",
"core.bonus.SOUL_STEAL.description": "当杀死敌人时获得 ${val} 数量",
"core.bonus.SPELLCASTER.name": "施法者",
"core.bonus.SPELLCASTER.description": "生物可以施放 ${subtype.spell}",
"core.bonus.SPELL_AFTER_ATTACK.name": "攻击后施法",
"core.bonus.SPELL_AFTER_ATTACK.description": "${val}% 攻击后施放 ${subtype.spell}",
"core.bonus.SPELL_BEFORE_ATTACK.name": "攻击前施法",
"core.bonus.SPELL_BEFORE_ATTACK.description": "${val}% 攻击前施放 ${subtype.spell}",
"core.bonus.SPELL_DAMAGE_REDUCTION.name": "魔法伤害抵抗",
"core.bonus.SPELL_DAMAGE_REDUCTION.description": "受魔法攻击时伤害减少 ${val}%",
"core.bonus.SPELL_IMMUNITY.name": "特定魔法免疫",
"core.bonus.SPELL_IMMUNITY.description": "免疫 ${subtype.spell}",
"core.bonus.SPELL_LIKE_ATTACK.name": "魔法攻击",
"core.bonus.SPELL_LIKE_ATTACK.description": "攻击时使用 ${subtype.spell}",
"core.bonus.SPELL_RESISTANCE_AURA.name": "抗魔光环",
"core.bonus.SPELL_RESISTANCE_AURA.description": "邻近部队获得 ${val}% 魔法抵抗",
"core.bonus.SUMMON_GUARDIANS.name": "召唤守卫",
"core.bonus.SUMMON_GUARDIANS.description": "战斗前召唤 ${subtype.creature} (${val}%)",
"core.bonus.SYNERGY_TARGET.name": "可协助攻击",
"core.bonus.SYNERGY_TARGET.description": "生物受到协助攻击的影响",
"core.bonus.TWO_HEX_ATTACK_BREATH.name": "龙息",
"core.bonus.TWO_HEX_ATTACK_BREATH.description": "吐息攻击2个部队",
"core.bonus.THREE_HEADED_ATTACK.name": "半环击",
"core.bonus.THREE_HEADED_ATTACK.description": "攻击正前方多个敌人",
"core.bonus.TRANSMUTATION.name": "变换",
"core.bonus.TRANSMUTATION.description": "${val}% 机会将敌人变成其他生物",
"core.bonus.UNDEAD.name": "不死生物",
"core.bonus.UNDEAD.description": "生物有丧尸属性",
"core.bonus.UNLIMITED_RETALIATIONS.name": "无限反击",
"core.bonus.UNLIMITED_RETALIATIONS.description": "每回合可以无限反击敌人",
"core.bonus.WATER_IMMUNITY.name": "水系免疫",
"core.bonus.WATER_IMMUNITY.description": "免疫水系魔法",
"core.bonus.WIDE_BREATH.name": "弧形焰息",
"core.bonus.WIDE_BREATH.description": "吐息攻击前方扇形6个部队"
}

View File

@ -1,6 +1,16 @@
{
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"chinese" : {
"name" : "VCMI essential files",
"description" : "Essential files required for VCMI to run correctly",
"skipValidation" : true,
"translations" : [
"config/vcmi/chinese.json"
]
},
"german" : {
"name" : "VCMI - grundlegende Dateien",

View File

@ -131,7 +131,11 @@ std::vector<std::string> MapAudioPlayer::getAmbientSounds(const int3 & tile)
{
const auto & object = CGI->mh->getMap()->objects[objectID.getNum()];
if(object->getAmbientSound())
assert(object);
if (!object)
logGlobal->warn("Already removed object %d found on tile! (%d %d %d)", objectID.getNum(), tile.x, tile.y, tile.z);
if(object && object->getAmbientSound())
result.push_back(object->getAmbientSound().get());
}
@ -194,8 +198,10 @@ MapAudioPlayer::MapAudioPlayer()
objects.resize(boost::extents[mapSize.z][mapSize.x][mapSize.y]);
for(const auto & obj : CGI->mh->getMap()->objects)
{
if (obj)
addObject(obj);
}
}
MapAudioPlayer::~MapAudioPlayer()

View File

@ -47,6 +47,10 @@ protected:
void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override;
void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) override {}
public:
MapAudioPlayer();
~MapAudioPlayer() override;

View File

@ -436,6 +436,9 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
{
BattleHex attackFromHex = owner.fieldController->fromWhichHexAttack(targetHex);
DamageEstimation estimation = owner.curInt->cb->battleEstimateDamage(owner.stacksController->getActiveStack(), targetStack, attackFromHex);
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
return formatMeleeAttack(estimation, targetStack->getName());
}
@ -444,6 +447,8 @@ std::string BattleActionsController::actionGetStatusMessage(PossiblePlayerBattle
const auto * shooter = owner.stacksController->getActiveStack();
DamageEstimation estimation = owner.curInt->cb->battleEstimateDamage(shooter, targetStack, shooter->getPosition());
estimation.kills.max = std::min<int64_t>(estimation.kills.max, targetStack->getCount());
estimation.kills.min = std::min<int64_t>(estimation.kills.min, targetStack->getCount());
return formatRangedAttack(estimation, targetStack->getName(), shooter->shots.available());
}

View File

@ -54,6 +54,7 @@ BattleInterface::BattleInterface(const CCreatureSet *army1, const CCreatureSet *
, attackerInt(att)
, defenderInt(defen)
, curInt(att)
, battleOpeningDelayActive(true)
{
if(spectatorInt)
{
@ -112,7 +113,7 @@ void BattleInterface::playIntroSoundAndUnlockInterface()
}
};
battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);
int battleIntroSoundChannel = CCS->soundh->playSoundFromSet(CCS->soundh->battleIntroSounds);
if (battleIntroSoundChannel != -1)
{
CCS->soundh->setCallback(battleIntroSoundChannel, onIntroPlayed);
@ -120,8 +121,15 @@ void BattleInterface::playIntroSoundAndUnlockInterface()
if (settings["gameTweaks"]["skipBattleIntroMusic"].Bool())
openingEnd();
}
else
else // failed to play sound
{
onIntroSoundPlayed();
}
}
bool BattleInterface::openingPlaying()
{
return battleOpeningDelayActive;
}
void BattleInterface::onIntroSoundPlayed()
@ -132,6 +140,19 @@ void BattleInterface::onIntroSoundPlayed()
CCS->musich->playMusicFromSet("battle", true, true);
}
void BattleInterface::openingEnd()
{
assert(openingPlaying());
if (!openingPlaying())
return;
onAnimationsFinished();
if(tacticsMode)
tacticNextStack(nullptr);
activateStack();
battleOpeningDelayActive = false;
}
BattleInterface::~BattleInterface()
{
CPlayerInterface::battleInt = nullptr;
@ -530,24 +551,6 @@ void BattleInterface::activateStack()
GH.fakeMouseMove();
}
bool BattleInterface::openingPlaying()
{
return battleIntroSoundChannel != -1;
}
void BattleInterface::openingEnd()
{
assert(openingPlaying());
if (!openingPlaying())
return;
onAnimationsFinished();
if(tacticsMode)
tacticNextStack(nullptr);
activateStack();
battleIntroSoundChannel = -1;
}
bool BattleInterface::makingTurn() const
{
return stacksController->getActiveStack() != nullptr;

View File

@ -111,8 +111,8 @@ class BattleInterface
/// defender interface, not null if attacker is human in our vcmiclient
std::shared_ptr<CPlayerInterface> defenderInt;
/// ID of channel on which battle opening sound is playing, or -1 if none
int battleIntroSoundChannel;
/// if set to true, battle is still starting and waiting for intro sound to end / key press from player
bool battleOpeningDelayActive;
void playIntroSoundAndUnlockInterface();
void onIntroSoundPlayed();

View File

@ -88,16 +88,13 @@ BattleStacksController::BattleStacksController(BattleInterface & owner):
static const auto shifterNegative = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 0.2f, 0.2f );
static const auto shifterNeutral = ColorFilter::genRangeShifter( 0.f, 0.f, 0.f, 1.0f, 1.0f, 0.2f );
amountNormal->adjustPalette(shifterNormal, 0);
amountPositive->adjustPalette(shifterPositive, 0);
amountNegative->adjustPalette(shifterNegative, 0);
amountEffNeutral->adjustPalette(shifterNeutral, 0);
// do not change border color
static const int32_t ignoredMask = 1 << 26;
//Restore border color {255, 231, 132, 255} to its original state
amountNormal->resetPalette(26);
amountPositive->resetPalette(26);
amountNegative->resetPalette(26);
amountEffNeutral->resetPalette(26);
amountNormal->adjustPalette(shifterNormal, ignoredMask);
amountPositive->adjustPalette(shifterPositive, ignoredMask);
amountNegative->adjustPalette(shifterNegative, ignoredMask);
amountEffNeutral->adjustPalette(shifterNeutral, ignoredMask);
std::vector<const CStack*> stacks = owner.curInt->cb->battleGetAllStacks(true);
for(const CStack * s : stacks)
@ -187,7 +184,7 @@ void BattleStacksController::stackReset(const CStack * stack)
void BattleStacksController::stackAdded(const CStack * stack, bool instant)
{
// Tower shooters have only their upper half visible
static const int turretCreatureAnimationHeight = 225;
static const int turretCreatureAnimationHeight = 232;
stackFacingRight[stack->ID] = stack->side == BattleSide::ATTACKER; // must be set before getting stack position
@ -201,6 +198,11 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
stackAnimation[stack->ID] = AnimationControls::getAnimation(turretCreature);
stackAnimation[stack->ID]->pos.h = turretCreatureAnimationHeight;
stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
// FIXME: workaround for visible animation of Medusa tails (animation disabled in H3)
if (turretCreature->idNumber == CreatureID::MEDUSA )
stackAnimation[stack->ID]->pos.w = 250;
coords = owner.siegeController->getTurretCreaturePosition(stack->initialPosition);
}
@ -209,10 +211,10 @@ void BattleStacksController::stackAdded(const CStack * stack, bool instant)
stackAnimation[stack->ID] = AnimationControls::getAnimation(stack->getCreature());
stackAnimation[stack->ID]->onAnimationReset += std::bind(&onAnimationFinished, stack, stackAnimation[stack->ID]);
stackAnimation[stack->ID]->pos.h = stackAnimation[stack->ID]->getHeight();
stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
}
stackAnimation[stack->ID]->pos.x = coords.x;
stackAnimation[stack->ID]->pos.y = coords.y;
stackAnimation[stack->ID]->pos.w = stackAnimation[stack->ID]->getWidth();
stackAnimation[stack->ID]->setType(ECreatureAnimType::HOLDING);
if (!instant)

View File

@ -343,13 +343,14 @@ static SDL_Color addColors(const SDL_Color & base, const SDL_Color & over)
void CreatureAnimation::genSpecialPalette(IImage::SpecialPalette & target)
{
target[0] = genShadow(shadowAlpha / 2);
target.resize(8);
target[0] = genShadow(0);
target[1] = genShadow(shadowAlpha / 2);
target[2] = genShadow(shadowAlpha);
target[3] = genShadow(shadowAlpha);
target[4] = genBorderColor(getBorderStrength(elapsedTime), border);
target[5] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[6] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
// colors 2 & 3 are not used in creatures
target[4] = genShadow(shadowAlpha);
target[5] = genBorderColor(getBorderStrength(elapsedTime), border);
target[6] = addColors(genShadow(shadowAlpha), genBorderColor(getBorderStrength(elapsedTime), border));
target[7] = addColors(genShadow(shadowAlpha / 2), genBorderColor(getBorderStrength(elapsedTime), border));
}
void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter, bool facingRight)
@ -371,8 +372,8 @@ void CreatureAnimation::nextFrame(Canvas & canvas, const ColorFilter & shifter,
IImage::SpecialPalette SpecialPalette;
genSpecialPalette(SpecialPalette);
image->setSpecialPallete(SpecialPalette);
image->adjustPalette(shifter, 8);
image->setSpecialPallete(SpecialPalette, IImage::SPECIAL_PALETTE_MASK_CREATURES);
image->adjustPalette(shifter, IImage::SPECIAL_PALETTE_MASK_CREATURES);
canvas.draw(image, pos.topLeft(), Rect(0, 0, pos.w, pos.h));

View File

@ -26,27 +26,27 @@ public:
virtual bool hasOngoingAnimations() = 0;
/// Plays fade-in animation and adds object to map
virtual void onObjectFadeIn(const CGObjectInstance * obj) {}
virtual void onObjectFadeIn(const CGObjectInstance * obj) = 0;
/// Plays fade-out animation and removed object from map
virtual void onObjectFadeOut(const CGObjectInstance * obj) {}
virtual void onObjectFadeOut(const CGObjectInstance * obj) = 0;
/// Adds object to map instantly, with no animation
virtual void onObjectInstantAdd(const CGObjectInstance * obj) {}
virtual void onObjectInstantAdd(const CGObjectInstance * obj) = 0;
/// Removes object from map instantly, with no animation
virtual void onObjectInstantRemove(const CGObjectInstance * obj) {}
virtual void onObjectInstantRemove(const CGObjectInstance * obj) = 0;
/// Perform hero movement animation, moving hero across terrain
virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
virtual void onHeroMoved(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
/// Perform initialization of hero teleportation animation with terrain fade animation
virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) {}
virtual void onBeforeHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroTeleported(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest){};
virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest){};
virtual void onBeforeHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroEmbark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest){};
virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest){};
virtual void onBeforeHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
virtual void onAfterHeroDisembark(const CGHeroInstance * obj, const int3 & from, const int3 & dest) = 0;
};

View File

@ -40,7 +40,8 @@ enum class EImageBlitMode : uint8_t
class IImage
{
public:
using SpecialPalette = std::array<SDL_Color, 7>;
using SpecialPalette = std::vector<SDL_Color>;
static constexpr int32_t SPECIAL_PALETTE_MASK_CREATURES = 0b11110011;
//draws image on surface "where" at position
virtual void draw(SDL_Surface * where, int posX = 0, int posY = 0, const Rect * src = nullptr) const = 0;
@ -65,7 +66,7 @@ public:
//only indexed bitmaps, 16 colors maximum
virtual void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) = 0;
virtual void adjustPalette(const ColorFilter & shifter, size_t colorsToSkip) = 0;
virtual void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) = 0;
virtual void resetPalette(int colorID) = 0;
virtual void resetPalette() = 0;
@ -73,7 +74,7 @@ public:
virtual void setBlitMode(EImageBlitMode mode) = 0;
//only indexed bitmaps with 7 special colors
virtual void setSpecialPallete(const SpecialPalette & SpecialPalette) = 0;
virtual void setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) = 0;
virtual void horizontalFlip() = 0;
virtual void verticalFlip() = 0;

View File

@ -308,7 +308,7 @@ void SDLImage::shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32
}
}
void SDLImage::adjustPalette(const ColorFilter & shifter, size_t colorsToSkip)
void SDLImage::adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask)
{
if(originalPalette == nullptr)
return;
@ -316,8 +316,11 @@ void SDLImage::adjustPalette(const ColorFilter & shifter, size_t colorsToSkip)
SDL_Palette* palette = surf->format->palette;
// Note: here we skip first colors in the palette that are predefined in H3 images
for(int i = colorsToSkip; i < palette->ncolors; i++)
for(int i = 0; i < palette->ncolors; i++)
{
if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
continue;
palette->colors[i] = shifter.shiftColor(originalPalette->colors[i]);
}
}
@ -340,11 +343,17 @@ void SDLImage::resetPalette( int colorID )
SDL_SetPaletteColors(surf->format->palette, originalPalette->colors + colorID, colorID, 1);
}
void SDLImage::setSpecialPallete(const IImage::SpecialPalette & SpecialPalette)
void SDLImage::setSpecialPallete(const IImage::SpecialPalette & specialPalette, uint32_t colorsToSkipMask)
{
if(surf->format->palette)
{
CSDL_Ext::setColors(surf, const_cast<SDL_Color *>(SpecialPalette.data()), 1, 7);
size_t last = std::min<size_t>(specialPalette.size(), surf->format->palette->ncolors);
for (size_t i = 0; i < last; ++i)
{
if(i < std::numeric_limits<uint32_t>::digits && ((colorsToSkipMask >> i) & 1) == 1)
surf->format->palette->colors[i] = specialPalette[i];
}
}
}

View File

@ -66,14 +66,14 @@ public:
void verticalFlip() override;
void shiftPalette(uint32_t firstColorID, uint32_t colorsToMove, uint32_t distanceToMove) override;
void adjustPalette(const ColorFilter & shifter, size_t colorsToSkip) override;
void adjustPalette(const ColorFilter & shifter, uint32_t colorsToSkipMask) override;
void resetPalette(int colorID) override;
void resetPalette() override;
void setAlpha(uint8_t value) override;
void setBlitMode(EImageBlitMode mode) override;
void setSpecialPallete(const SpecialPalette & SpecialPalette) override;
void setSpecialPallete(const SpecialPalette & SpecialPalette, uint32_t colorsToSkipMask) override;
friend class SDLImageLoader;

View File

@ -1210,7 +1210,8 @@ CCastleInterface::CCastleInterface(const CGTownInstance * Town, const CGTownInst
CCastleInterface::~CCastleInterface()
{
adventureInt->onAudioResumed();
if (adventureInt) // may happen on exiting client with open castle interface
adventureInt->onAudioResumed();
if(LOCPLINT->castleInt == this)
LOCPLINT->castleInt = nullptr;
}

View File

@ -924,6 +924,9 @@ void CMarketplaceWindow::artifactsChanged(bool Left)
toRemove.insert(item);
removeItems(toRemove);
// clear set to erase final instance of shared_ptr - we want to redraw screen only after it has been deleted
toRemove.clear();
redraw();
}

View File

@ -85,6 +85,7 @@ std::shared_ptr<CPicture> CWindowObject::createBg(std::string imageName, bool pl
return nullptr;
auto image = std::make_shared<CPicture>(imageName);
image->getSurface()->setBlitMode(EImageBlitMode::OPAQUE);
if(playerColored)
image->colorize(LOCPLINT->playerID);
return image;

View File

@ -68,7 +68,7 @@
"alpha" : 0.0
},
{
"time" : 0.2
"time" : 0.5
},
],
"teleportFadeOut" : [
@ -76,7 +76,7 @@
"time" : 0.0
},
{
"time" : 0.2,
"time" : 0.5,
"alpha" : 0.0
},
],

View File

@ -64,12 +64,12 @@
},
"language" : {
"type":"string",
"enum" : [ "chinese", "english", "german", "polish", "russian", "spanish", "ukrainian" ],
"enum" : [ "english", "chinese", "german", "polish", "russian", "spanish", "ukrainian" ],
"default" : "english"
},
"gameDataLanguage" : {
"type":"string",
"enum" : [ "auto", "chinese", "english", "german", "korean", "polish", "russian", "spanish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ],
"enum" : [ "auto", "english", "chinese", "german", "korean", "polish", "russian", "spanish", "ukrainian", "other_cp1250", "other_cp1251", "other_cp1252" ],
"default" : "auto"
},
"lastSave" : {

View File

@ -684,7 +684,7 @@ void CModInfo::loadLocalData(const JsonNode & data)
{
bool validated = false;
implicitlyEnabled = true;
explicitlyEnabled = true;
explicitlyEnabled = !config["keepDisabled"].Bool();
checksum = 0;
if (data.getType() == JsonNode::JsonType::DATA_BOOL)
{

View File

@ -1120,6 +1120,7 @@ public:
LICHES = 64,
BONE_DRAGON = 68,
TROGLODYTES = 70,
MEDUSA = 76,
HYDRA = 110,
CHAOS_HYDRA = 111,
AIR_ELEMENTAL = 112,
@ -1245,7 +1246,7 @@ class ObstacleInfo;
class Obstacle : public BaseForID<Obstacle, si32>
{
INSTID_LIKE_CLASS_COMMON(Obstacle, si32)
DLL_LINKAGE const ObstacleInfo * getInfo() const;
DLL_LINKAGE operator std::string() const;
DLL_LINKAGE static Obstacle fromString(const std::string & identifier);

View File

@ -1004,12 +1004,19 @@ std::set<BattleHex> CBattleInfoCallback::getStoppers(BattlePerspective::BattlePe
for(auto &oi : battleGetAllObstacles(whichSidePerspective))
{
if(battleIsObstacleVisibleForSide(*oi, whichSidePerspective))
if(!battleIsObstacleVisibleForSide(*oi, whichSidePerspective))
continue;
for(const auto & hex : oi->getStoppingTile())
{
range::copy(oi->getStoppingTile(), vstd::set_inserter(ret));
if(hex == ESiegeHex::GATE_BRIDGE && oi->obstacleType == CObstacleInstance::MOAT)
{
if(battleGetGateState() == EGateState::OPENED || battleGetGateState() == EGateState::DESTROYED)
continue; // this tile is disabled by drawbridge on top of it
}
ret.insert(hex);
}
}
return ret;
}