From 62029a8ea9a5c637de6fef9d5eba2a1069be9e59 Mon Sep 17 00:00:00 2001 From: zwwooooo Date: Wed, 11 Jan 2023 14:31:08 +0800 Subject: [PATCH 01/24] Chinese translation update(by TBS) --- Utils/_ChineseText.cpp | 8 ++++---- Utils/_Ja25ChineseText.cpp | 16 ++++++++-------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index bf3a913f4..8ab30522e 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -3226,11 +3226,11 @@ STR16 gzMercSkillTextNew[] = // new minor traits L"无线电操作员", // 21 L"告发", // 22 - L"生还者", //L"Survival" + L"向导", //L"Survival" // second names for major skills L"机枪手", // 24 - L"掷弹兵", + L"枪炮专家", //L"Bombardier", L"狙击手", L"游骑兵", L"枪斗术", @@ -3252,7 +3252,7 @@ STR16 gzMercSkillTextNew[] = L"间谍", // 43 L"Placeholder", // for radio operator (minor trait) L"Placeholder", // for snitch (minor trait) - L"生还者", // for survival (minor trait) + L"向导", // for survival (minor trait) L"更多...", // 47 L"情报", //L"Intel", for INTEL L"伪装", //L"Disguise", for DISGUISE @@ -7826,7 +7826,7 @@ STR16 MissingIMPSkillsDescriptions[] = // Radio Operator L"无线电操作员:你通过使用通讯设备让队伍的战略和战术水平得到了提升。 ± ", //L"Radio Operator: Your usage of communication devices broaden your team's tactical and strategic skills. ± ", // Survival - L"生还者: 大自然是你第二个家。 ± ", //L"Survival: Nature is a second home to you. ± ", + L"向导: 大自然是你第二个家。 ± ", //L"Survival: Nature is a second home to you. ± ", }; STR16 NewInvMessage[] = diff --git a/Utils/_Ja25ChineseText.cpp b/Utils/_Ja25ChineseText.cpp index 6c60c9724..bfa02af40 100644 --- a/Utils/_Ja25ChineseText.cpp +++ b/Utils/_Ja25ChineseText.cpp @@ -59,7 +59,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= L"I.M.P 主要特殊技能",// L"I.M.P. Major Traits", // second names L"机枪手",// L"Machinegunner", - L"掷弹兵",// L"Bombardier", + L"枪炮专家",// L"Bombardier", L"狙击手",// L"Sniper", L"游骑兵",// L"Ranger", L"枪斗术",// L"Gunfighter", @@ -74,7 +74,7 @@ STR16 gzIMPSkillTraitsTextNewMajor[]= STR16 gzIMPSkillTraitsTextNewMinor[]= { L"双持",// L"Ambidextrous", - L"格斗",// L"Melee", + L"近战",// L"Melee", L"投掷",// L"Throwing", L"夜战",// L"Night Ops", L"潜行",// L"Stealthy", @@ -84,7 +84,7 @@ STR16 gzIMPSkillTraitsTextNewMinor[]= L"教学",// L"Teaching", L"侦察",// L"Scouting", L"无线电操作员", //L"Radio Operator", - L"生还者", //L"Survival", + L"向导", //L"Survival", L"无",// L"None", L"I.M.P 副技能",// L"I.M.P. Minor Traits", @@ -450,18 +450,18 @@ STR16 gzIMPOldSkillTraitsHelpTexts[]= STR16 gzIMPNewCharacterTraitsHelpTexts[]= { L"优点:无。\n缺点:无。",// L"A: No advantage.\nD: No disadvantage.", - L"优点:身边有多个雇佣兵时表现最佳。\n缺点:孤单一人时士气不会上升。",// L"A: Has better performance when couple of mercs are nearby.\nD: Gains no morale when no other merc is nearby.", + L"优点:身边有多个佣兵时表现最佳。\n缺点:孤单一人时士气不会上升。",// L"A: Has better performance when couple of mercs are nearby.\nD: Gains no morale when no other merc is nearby.", L"优点:独自行动时表现最好。\n缺点:在团队中士气不会上升。",// L"A: Has better performance when no other merc is nearby.\nD: Gains no morale when in a group.", L"优点:士气上升得快,下降得慢。\n缺点:发现地雷和陷阱的几率降低了。",// L"A: His morale sinks a little slower and grows faster than normal.\nD: Has lesser chance to detect traps and mines.", L"优点:更善于训练民兵和跟别人交流。\n缺点:士气不会因为其他队员的行为而上升。",// L"A: Has bonus on training militia and is better at communication with people.\nD: Gains no morale for actions of other mercs.", - L"优点:自我锻炼或学习效率略高。\n缺点:减少对恐惧和火力压制的承受力。",// L"A: Slightly faster learning when assigned on practicing or as a student.\nD: Has lesser suppression and fear resistance.", + L"优点:自我锻炼或学习效率略微提高。\n缺点:减少对恐惧和火力压制的承受力。",// L"A: Slightly faster learning when assigned on practicing or as a student.\nD: Has lesser suppression and fear resistance.", L"优点:减少一切行动的体能消耗(除了医疗、修理、训练民兵和学习某种技能)。\n缺点:智力、领导能力、爆破、修理和医疗技术提高得慢一些。",// L"A: His energy goes down a bit slower except on assignments as doctor, repairman, militia trainer or if learning certain skills.\nD: His wisdom, leadership, explosives, mechanical and medical skills improve slightly slower.", - L"优点:点射/自动模式下命中率,近战杀伤力和消灭敌人所得的时期更高。\n缺点:需要耐心的行为会得到效率惩罚,比如修理、开锁、解除陷阱、医治和训练民兵。",// L"A: Has slightly better chance to hit on burst/autofire and inflicts slightly bigger damage in close combat\n Gains a little more morale for killing.\nD: Has penalty for actions which needs patience like repairing items, picking locks, removing traps, doctoring, training militia.", + L"优点:点射/自动模式下命中率,近战杀伤力和消灭敌人所得的期望更高。\n缺点:需要耐心的行为会得到效率惩罚,比如修理、开锁、解除陷阱、医治和训练民兵。",// L"A: Has slightly better chance to hit on burst/autofire and inflicts slightly bigger damage in close combat\n Gains a little more morale for killing.\nD: Has penalty for actions which needs patience like repairing items, picking locks, removing traps, doctoring, training militia.", L"优点:需要耐心的工作会得到效率奖励,比如修理、撬锁、解除陷阱、医治和训练民兵。\n缺点:略微降低中断率。",// L"A: Has bonus for actions which needs patience like repairing items, picking locks, removing traps, doctoring and training militia.\nD: His interrupts chance is slightly lowered.", - L"优点:增加对火力压制和恐惧的承受力,\n负伤其他雇佣兵阵亡对他的士气影响的也比较小。\n缺点:容易成为敌人的移动靶。",// L"A: Incresed resistance to suppression and fear.\n Morale loss for taking damage and companions deaths is lower for him.\nD: Can be hit easier and enemy penalty for moving target is lesser in his case.", + L"优点:增加对火力压制和恐惧的承受力。\n负伤其他佣兵阵亡对他的士气影响的也比较小。\n缺点:容易成为敌人的移动靶。",// L"A: Incresed resistance to suppression and fear.\n Morale loss for taking damage and companions deaths is lower for him.\nD: Can be hit easier and enemy penalty for moving target is lesser in his case.", L"优点:进行非战斗任务时士气会提升(除了训练民兵)。\n缺点:杀人不增加士气。",// L"A: He gains morale when on non-combat assignments (except training militia).\nD: Gains no morale for killing.", L"优点:攻击有更高的几率能造成受害者属性值降低,和更严重的创伤,同时提高自己的士气。\n缺点:与其他人交流是个问题,脱离战斗后士气也会快速下沉。",// L"A: Has bigger chance for inflicting stat loss and can inflict special painful wounds when able to\n Gains bonus morale for inflicting stat loss.\nD: Has penalty for communication with people and his morale sinks faster if not fighting.", - L"优点:附近有异性雇佣兵时表现更佳。\n缺点:附近所有同性雇佣兵士气提升得慢。",// L"A: Has better performance when there are some mercs of opposite gender nearby.\nD: Morale of other mercs of the same gender grows slower if nearby.", + L"优点:附近有异性佣兵时表现更佳。\n缺点:附近所有同性佣兵士气提升得慢。",// L"A: Has better performance when there are some mercs of opposite gender nearby.\nD: Morale of other mercs of the same gender grows slower if nearby.", L"优点:撤退时士气增加。\n缺点:敌众我寡时,会降低士气。", //L"A: Gains morale when retreating.\nD: Loses morale when encountering numerically superior enemy forces.", }; From 0ac6c3eb2ded7b1dbf9ba460a0bd8d7687edc1ef Mon Sep 17 00:00:00 2001 From: zwwooooo Date: Wed, 12 Apr 2023 13:54:16 +0800 Subject: [PATCH 02/24] Chinese translation update(by TBS) --- Utils/_ChineseText.cpp | 30 +++++++++++++++--------------- Utils/_Ja25ChineseText.cpp | 20 ++++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index 695fc5929..68d32c8bc 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -6213,7 +6213,7 @@ STR16 zOptionsText[] = STR16 z113FeaturesScreenText[] = { L"1.13 特征功能", //L"1.13 FEATURE TOGGLES", - L"在游戏中更改这些选项将影响您的游戏体验。", //L"Changing these settings during a campaign will affect your experience.", + L"在游戏中更改这些选项将影响您的游戏体验。(更改后必须重新开始新游戏)", //L"Changing these settings during a campaign will affect your experience.", L"将鼠标悬停在功能按钮上以获得更多信息。某些功能需要在JA2_Options.ini(或其他文件)中设置。", //L"Hover over a feature to display more information. Some features may be configurable in JA2_Options.ini (or other specified file).", }; @@ -6532,7 +6532,7 @@ STR16 zOptionsToggleText[] = L"实时确认", //"Real Time Confirmation", L"显示睡觉/醒来时的提示", //"Display sleep/wake notifications", L"使用公制系统", //"Use Metric System", - L"佣兵假光", //"Highlight Mercs", + L"高亮显示佣兵", //"Highlight Mercs", L"锁定佣兵", //"Snap Cursor to Mercs", L"锁定门", //"Snap Cursor to Doors", L"物品闪亮", //"Make Items Glow", @@ -6574,8 +6574,8 @@ STR16 zOptionsToggleText[] = L"反转鼠标滚轮", //L"Invert mouse wheel", L"保持佣兵间距", // when multiple mercs are selected, they will try to keep their relative distances L"显示已知敌人位置", //L"Show enemy location", show locator on last known enemy location - L"Start at maximum aim", - L"Alternative pathfinding", + L"准心开始时为最大", // L"Start at maximum aim", + L"替换新的寻路方式", // L"Alternative pathfinding", L"--作弊模式选项--", // TOPTION_CHEAT_MODE_OPTIONS_HEADER, L"强制 Bobby Ray 送货", // force all pending Bobby Ray shipments L"-----------------", // TOPTION_CHEAT_MODE_OPTIONS_END @@ -6637,7 +6637,7 @@ STR16 zOptionsScreenHelpText[] = L"打开时,使用公制系统,否则使用英制系统。", //Highlight Mercs - L"打开时,切换虚拟佣兵照明。(|G)", + L"打开时,虚拟灯光会照亮佣兵周围。(|G)", //L"When ON, highlights the mercenary (Not visible to enemies).\nToggle in-game with (|G)", //Smart cursor L"打开时,光标移动到佣兵身上时会高亮显示佣兵。", @@ -6693,10 +6693,10 @@ STR16 zOptionsScreenHelpText[] = L"打开时,会直接显示该区域最后一个敌人的大致位置。", L"打开时,在区域物品栏界面,右键点击装有物品的携行具时可直接显示包含的物品。", L"打开时,反转鼠标滚轮方向。", - L"打开时,当选择多个佣兵,在前进时会保持彼此的间距。|C|t|r|l+|A|l|t+|G \n(按|S|h|i|f|t+点击人物头像可以加入或移出队伍)", //L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", TODO.Translate + L"打开时,当选择多个佣兵,在前进时会保持彼此的间距。|C|t|r|l+|A|l|t+|G \n(按|S|h|i|f|t+点击人物头像可以加入或移出队伍)", //L"When ON and multiple mercs are selected, they will try to keep their relative distances while moving.\n(press |C|t|r|l+|A|l|t+|G to toggle mode or |S|h|i|f|t + click to move in formation)", L"打开时,会显示已知敌人最后移动的位置。", //L"When ON, shows last known enemy location.", - L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", - L"When ON, Use A* pathfinding algorithm, instead of original", + L"打开时,默认瞄准值为最大,而不是无。", //L"When ON, aiming at enemy will start at maximum aiming instead of default no aim", + L"打开时,使用A*寻路算法,而不是原始算法。", //L"When ON, Use A* pathfinding algorithm, instead of original", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_HEADER", L"强制 Bobby Ray 出货", L"(text not rendered)TOPTION_CHEAT_MODE_OPTIONS_END", @@ -7032,7 +7032,7 @@ STR16 pMessageStrings[] = L"磁盘空间不足。只有%sMB可用空间,《铁血联盟2》需要%sMB。", L"从AIM雇佣了%s。", //"Hired %s from AIM", L"%s抓住了%s。", //"%s has caught %s.", //'Merc name' has caught 'item' -- let SirTech know if name comes after item. - L"%s使用了(拾取)%s。", //L"%s has taken %s。", + L"%s(使用了、拾取了、中了)%s。", //L"%s has taken %s。", L"%s没有医疗技能", //"%s has no medical skill",//'Merc name' has no medical skill. //CDRom errors (such as ejecting CD while attempting to read the CD) @@ -7131,7 +7131,7 @@ STR16 pMessageStrings[] = L"虚拟佣兵光照开启", L"虚拟佣兵光照关闭", L"军队%s活动。", //L"Squad %s active.", - L"%s作弊者%s。", //L"%s smoked %s.", + L"%s抽了只%s。", //L"%s smoked %s.", L"激活作弊?", //L"Activate cheats?", L"关闭作弊?", //L"Deactivate cheats?", }; @@ -8133,7 +8133,7 @@ STR16 gzIMPDisabilityTraitText[]= L"身心健全", L"怕热", L"神经质", - L"幽闭恐慌症", + L"幽闭恐惧症", L"旱鸭子", L"怕虫", L"健忘", @@ -9198,8 +9198,8 @@ STR16 szPrisonerTextStr[]= L"一个高阶军官%s被发现!", //L"A high-ranking army officer in %s has been revealed!", L"敌方领袖拒绝考虑投降!", //L"The enemy leader refuses to even consider surrender!", L"%d名囚犯自愿加入我军。", //L"%d prisoners volunteered to join our forces.", - L"Some of your mercs managed to escape the enemy capture!", - L"No possible escape is seen, it's a fight to the death!" + L"你的佣兵成功逃脱了敌人的追捕。", //L"Some of your mercs managed to escape the enemy capture!", + L"没有逃跑与投降,只有死战到底!", //L"No possible escape is seen, it's a fight to the death!", }; STR16 szMTATextStr[]= @@ -11997,7 +11997,7 @@ STR16 szRebelCommandHelpText[] = L"|指|挥|部\n \n指挥部一旦部署,就会负责处理\n该区域内的日常事务。包括支持当地人,制造\n反抗宣传,制定地区政策等等。", //L"|A|d|m|i|n|i|s|t|r|a|t|i|o|n |T|e|a|m\n \nOnce deployed, an admin team is responsible for handling the\nday-to-day affairs of the region. This includes supporting\nlocals, creating rebel propaganda, establishing regional\npolicies, and more.", L"|忠|诚|度\n \n许多行政命令的有效性取决于\n该地区的忠诚度,提高忠诚度\n能得到最大利益化。", //L"|L|o|y|a|l|t|y\n \nThe effectiveness of many Administrative Actions depends on\nthe region's loyalty to your cause. It is in your best interest\nto raise loyalty as high as possible.", L"|最|高|忠|诚|度\n \n你需要说服当地人完全信任你。这可以\n通过为他们建立物资供应来实现,表明\n你打算改善他们的生活质量。", //L"|M|a|x|i|m|u|m |L|o|y|a|l|t|y\n \nYou will need to convince the locals to fully trust you. This\ncan be done by creating a supply line to them, showing that\nyou intend to improve their quality of life.", - L"该管理行动只会作用到城镇区域。", //L"This Admin Action applies its bonus to town sectors only.", TODO.Translate + L"该管理行动只会作用到城镇区域。", //L"This Admin Action applies its bonus to town sectors only.", L"该管理行动会作用到城镇区域。\n和直接相邻的区域。", //L"This Admin Action applies its bonus to town sectors, and\nsectors immediately adjacent to them.", L"该管理行动会作用到城镇区域。\n1级覆盖周边1个区域。\n2级覆盖周边2个区域。", //L"This Admin Action applies its bonus to town sectors, one\nsector away at Tier 1, and up to two sectors away at Tier 2.", L"该管理行动会作用到城镇区域。\n1级覆盖周边2个区域。\n2级覆盖周边3个区域。", //L"This Admin Action applies its bonus to town sectors, up to\ntwo sectors away at Tier 1, and up to three sectors away at Tier 2.", @@ -12111,7 +12111,7 @@ STR16 szRebelCommandAgentMissionsText[] = L"临时增加对这个城镇的直接援助。城镇忠诚度会在任务期间被动提升。", //L"Temporarily increase direct support to this town. Town loyalty passively increases for the duration of the mission.", L"士兵悬赏(Kingpin)", //L"Soldier Bounties (Kingpin)", L"杀敌以获得资金奖励。和Kingpin谈谈,他感觉可以利用你的存在来削弱女王的权威。奖金会在午夜存入你的账户,每天最多$%d。", //L"Get a payout for enemy kills. Negotiate with Kingpin, who feels he can use your presence here to indirectly weaken the Queen's power. Bounties are deposited into your account at midnight and are limited to $%d per day.", - L"在任意地方训练民兵", //L"Train Militia Anywhere", + L"在城镇外任意地区训练民兵", //L"Train Militia Anywhere", L"野外训练区是可以快速设立和拆毁的。民兵可以在城镇外的非交战区接受训练。", //L"Create training areas in the wilderness that can be quickly set up and torn down. Militia can be trained in uncontested sectors outside of town.", }; diff --git a/Utils/_Ja25ChineseText.cpp b/Utils/_Ja25ChineseText.cpp index bfa02af40..1a6feedd4 100644 --- a/Utils/_Ja25ChineseText.cpp +++ b/Utils/_Ja25ChineseText.cpp @@ -467,18 +467,18 @@ STR16 gzIMPNewCharacterTraitsHelpTexts[]= STR16 gzIMPDisabilitiesHelpTexts[]= { - L"没有其他附加效果。",// L"No effects.", - L"可能会在热带和沙漠地区呼吸困难,降低综合表现。",// L"Has problems with breathing and reduced overall performance if in tropical or desert sectors.", - L"可能会孤单一人在某些情况下感到恐慌。",// L"Can suffer panic attack if left alone in certain situations.", - L"可能会进入地下后降低综合表现。",// L"His overall performance is reduced if underground.", - L"可能会在游泳时轻易的溺水。",// L"If trying to swim he can easily drown.", - L"可能会在看到大虫子后六神无主,也会在热带地区降低综合表现。",// L"A look at large insects can make a big problems\nand being in tropical sectors also reduce his performance a bit.", - L"可能会偶尔忘记手头的任务,也会在战斗中损失行动点。",// L"Sometimes forgets what orders he got and therefore loses some APs if in combat.", - L"可能会偶尔发疯并把手里武器设在自动后乱喷。\n如果武器不能自动射击将会打击自身士气。",// L"He can go psycho and shoot like mad once per a while\nand can lose morale if unable to do that with given weapon.", + L"没有其它附加效果。",// L"No effects.", + L"在热带或沙漠区域,会出现呼吸问题降低综合表现。",// L"Has problems with breathing and reduced overall performance if in tropical or desert sectors.", + L"独自一人时可能会感到恐慌。",// L"Can suffer panic attack if left alone in certain situations.", + L"在封闭空间或地下时会降低综合表现。",// L"His overall performance is reduced if underground.", + L"在游泳时会轻易的溺水。",// L"If trying to swim he can easily drown.", + L"看到大虫子后会六神无主,也会在热带地区降低综合表现。",// L"A look at large insects can make a big problems\nand being in tropical sectors also reduce his performance a bit.", + L"偶尔会忘记手头的任务,也会在战斗中损失些行动点。",// L"Sometimes forgets what orders he got and therefore loses some APs if in combat.", + L"偶尔会发疯并把手里的武器设为自动乱喷。\n如果武器不能自动射击将会打击自身士气。",// L"He can go psycho and shoot like mad once per a while\nand can lose morale if unable to do that with given weapon.", L"大大减少听力范围。", // L"Drastically reduced hearing.", L"减少视力范围。", // L"Reduced sight range.", - L"大大增加的流血速度。", //L"Drastically increased bleeding.", - L"在房顶上会降低战斗力。", //L"Performance suffers while on a rooftop.", + L"大大增加流血速度。", //L"Drastically increased bleeding.", + L"在房顶作战时会降低战斗力。", //L"Performance suffers while on a rooftop.", L"时不时自残。", //L"Occasionally harms self.", }; From 5400fb019dcd4c97f7445474fa73cc94ce720981 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Sat, 19 Aug 2023 22:30:57 +0200 Subject: [PATCH 03/24] Loading screen multiple resolutions support and stretch modes, loading bar fixes --- GameSettings.cpp | 4 +- GameSettings.h | 1 + Loading Screen.cpp | 127 ++++++++++++++++++++++++--------- Loading Screen.h | 2 + SaveLoadGame.cpp | 18 ++--- Utils/Animated ProgressBar.cpp | 57 ++++++++++----- 6 files changed, 149 insertions(+), 60 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index 3791ae0c7..349fa561c 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1147,7 +1147,9 @@ void LoadGameExternalOptions() giTimerIntervals[ NEXTSCROLL ] = (INT16)(giTimerIntervals[ NEXTSCROLL ] / gGameExternalOptions.fScrollSpeedFactor); gGameExternalOptions.gfUseExternalLoadscreens = iniReader.ReadBoolean("Graphics Settings","USE_EXTERNALIZED_LOADSCREENS", FALSE); - + + gGameExternalOptions.ubLoadscreenStretchMode = iniReader.ReadInteger("Graphics Settings", "LOADSCREEN_STRETCH_MODE", 0, 0, 2); + if (!is_networked) gGameExternalOptions.gfUseLoadScreenHints = iniReader.ReadBoolean("Graphics Settings","USE_LOADSCREENHINTS", TRUE); else diff --git a/GameSettings.h b/GameSettings.h index 5bdeb6dcf..523066f55 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -781,6 +781,7 @@ typedef struct INT32 ubEnemiesItemDrop; BOOLEAN gfUseExternalLoadscreens; + UINT32 ubLoadscreenStretchMode; // added by anv BOOLEAN gfUseLoadScreenHints; // added by Flugente UINT32 ubAdditionalDelayUntilLoadScreenDisposal; // added by WANNE to have time to read the load screen hints diff --git a/Loading Screen.cpp b/Loading Screen.cpp index a0641d3b5..2650cc9fb 100644 --- a/Loading Screen.cpp +++ b/Loading Screen.cpp @@ -23,6 +23,7 @@ extern BOOLEAN gfSchedulesHosed; #include "Ja25 Strategic Ai.h" #endif UINT8 gubLastLoadingScreenID = LOADINGSCREEN_NOTHING; +FLOAT fLoadingScreenAspectRatio; //BOOLEAN bShowSmallImage = FALSE; SECTOR_LOADSCREENS gSectorLoadscreens[MAX_SECTOR_LOADSCREENS]; @@ -343,6 +344,62 @@ static void BuildLoadscreenFilename(std::string& dst, const char* path, int reso dst.append(".sti"); } +std::string GetResolutionSuffix(SCREEN_RESOLUTION resolution) +{ + switch (resolution) + { + case _960x540: return "_960x540"; + case _800x600: return "_800x600"; + case _1024x600: return "_1024x600"; + case _1280x720: return "_1280x720"; + case _1024x768: return "_1024x768"; + case _1280x768: return "_1280x768"; + case _1360x768: return "_1360x768"; + case _1366x768: return "_1366x768"; + case _1280x800: return "_1280x800"; + case _1440x900: return "_1440x900"; + case _1600x900: return "_1600x900"; + case _1280x960: return "_1280x960"; + case _1440x960: return "_1440x960"; + case _1770x1000: return "_1770x1000"; + case _1280x1024: return "_1280x1024"; + case _1360x1024: return "_1360x1024"; + case _1600x1024: return "_1600x1024"; + case _1440x1050: return "_1440x1050"; + case _1680x1050: return "_1680x1050"; + case _1920x1080: return "_1920x1080"; + case _1600x1200: return "_1600x1200"; + case _1920x1200: return "_1920x1200"; + case _2560x1440: return "_2560x1440"; + case _2560x1600: return "_2560x1600"; + default: return ""; + } +} + +std::string FindBestFittingLoadscreenFilename(const std::string& baseName, SCREEN_RESOLUTION resolution) +{ + for (SCREEN_RESOLUTION res = resolution; res <= _2560x1600; res = (SCREEN_RESOLUTION)(res + 1)) + { + std::string fileName = baseName + GetResolutionSuffix(res); + if (FileExists((CHAR8*)((fileName + ".png").c_str()))) + { + return fileName + ".png"; + } + + if (FileExists((CHAR8*)((fileName + ".sti").c_str()))) + { + return fileName + ".sti"; + } + } + + if (FileExists((CHAR8*)((baseName + ".png").c_str()))) + { + return baseName + ".png"; + } + + return baseName + ".sti"; +} + //sets up the loadscreen with specified ID, and draws it to the FRAME_BUFFER, //and refreshing the screen with it. void DisplayLoadScreenWithID( UINT8 ubLoadScreenID ) @@ -420,27 +477,8 @@ void DisplayLoadScreenWithID( UINT8 ubLoadScreenID ) } } - std::string strImage; - - BuildLoadscreenFilename(strImage, imagePath.c_str(), 0, imageFormat.c_str()); - strImage.copy(vs_desc.ImageFile, sizeof(vs_desc.ImageFile)-1); - - - if ( !FileExists(vs_desc.ImageFile) ) - { - std::string strImage; - BuildLoadscreenFilename(strImage, imagePath.c_str(), 0, "png"); - strImage.copy(vs_desc.ImageFile, sizeof(vs_desc.ImageFile) - 1); - - if (!FileExists(vs_desc.ImageFile)) - { - std::string strImage("LOADSCREENS\\"); - - BuildLoadscreenFilename(strImage, LoadScreenNames[1], 0, imageFormat.c_str()); - - strImage.copy(vs_desc.ImageFile, sizeof(vs_desc.ImageFile) - 1); - } - } + std::string strImage = FindBestFittingLoadscreenFilename(imagePath, (SCREEN_RESOLUTION)iResolution); + strImage.copy(vs_desc.ImageFile, sizeof(vs_desc.ImageFile) - 1); } else { @@ -475,18 +513,41 @@ void DisplayLoadScreenWithID( UINT8 ubLoadScreenID ) //Blit the background image GetVideoSurface(&hVSurface, uiLoadScreen); - - // Stretch the background image - SrcRect.iLeft = 0; - SrcRect.iTop = 0; - SrcRect.iRight = hVSurface->usWidth; - SrcRect.iBottom = hVSurface->usHeight; - - DstRect.iLeft = 0; - DstRect.iTop = 0; - DstRect.iRight = SCREEN_WIDTH; - DstRect.iBottom = SCREEN_HEIGHT; - + + fLoadingScreenAspectRatio = (FLOAT)hVSurface->usWidth / (FLOAT)hVSurface->usHeight; + FLOAT fScreenAspectRatio = (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT; + + if (gGameExternalOptions.ubLoadscreenStretchMode == 1 || + (gGameExternalOptions.ubLoadscreenStretchMode == 2 && fLoadingScreenAspectRatio > fScreenAspectRatio)) + { + // match height, preserve aspect ratio + INT32 iCalculatedWidth = (INT32)(SCREEN_HEIGHT * fLoadingScreenAspectRatio + 0.5f); + + SrcRect.iLeft = 0; + SrcRect.iTop = 0; + SrcRect.iRight = hVSurface->usWidth; + SrcRect.iBottom = hVSurface->usHeight; + + DstRect.iLeft = (SCREEN_WIDTH - iCalculatedWidth) / 2; + DstRect.iTop = 0; + DstRect.iRight = SCREEN_WIDTH - ((SCREEN_WIDTH - iCalculatedWidth) / 2); + DstRect.iBottom = SCREEN_HEIGHT; + } + else + { + // vanilla (stretch to fit) + // Stretch the background image + SrcRect.iLeft = 0; + SrcRect.iTop = 0; + SrcRect.iRight = hVSurface->usWidth; + SrcRect.iBottom = hVSurface->usHeight; + + DstRect.iLeft = 0; + DstRect.iTop = 0; + DstRect.iRight = SCREEN_WIDTH; + DstRect.iBottom = SCREEN_HEIGHT; + } + BltStretchVideoSurface( FRAME_BUFFER, uiLoadScreen, 0, 0, 0, &SrcRect, &DstRect ); DeleteVideoSurfaceFromIndex( uiLoadScreen ); diff --git a/Loading Screen.h b/Loading Screen.h index fa5bc658b..831c9e2be 100644 --- a/Loading Screen.h +++ b/Loading Screen.h @@ -84,6 +84,8 @@ enum //For use by the game loader, before it can possibly know the situation. extern UINT8 gubLastLoadingScreenID; +extern FLOAT fLoadingScreenAspectRatio; + //returns the UINT8 ID for the specified sector. UINT8 GetLoadScreenID( INT16 sSectorX, INT16 sSectorY, INT8 bSectorZ ); diff --git a/SaveLoadGame.cpp b/SaveLoadGame.cpp index 5c245e846..844e49d61 100644 --- a/SaveLoadGame.cpp +++ b/SaveLoadGame.cpp @@ -6694,15 +6694,6 @@ BOOLEAN LoadSavedGame( int ubSavedGameID ) //Reset the Ai Timer clock giRTAILastUpdateTime = 0; - //if we are in tactical - if( guiScreenToGotoAfterLoadingSavedGame == GAME_SCREEN ) - { - //Initialize the current panel - InitializeCurrentPanel( ); - - SelectSoldier( gusSelectedSoldier, FALSE, TRUE ); - } - uiRelEndPerc += 1; SetRelativeStartAndEndPercentage( 0, uiRelStartPerc, uiRelEndPerc, L"Final Checks..." ); RenderProgressBar( 0, 100 ); @@ -6831,6 +6822,15 @@ BOOLEAN LoadSavedGame( int ubSavedGameID ) RemoveLoadingScreenProgressBar(); + //if we are in tactical + if (guiScreenToGotoAfterLoadingSavedGame == GAME_SCREEN) + { + //Initialize the current panel + InitializeCurrentPanel(); + + SelectSoldier(gusSelectedSoldier, FALSE, TRUE); + } + // sevenfm: reset sound map ResetSoundMap(); diff --git a/Utils/Animated ProgressBar.cpp b/Utils/Animated ProgressBar.cpp index 652d5f05e..5cdfa0ab6 100644 --- a/Utils/Animated ProgressBar.cpp +++ b/Utils/Animated ProgressBar.cpp @@ -14,6 +14,7 @@ #include "WordWrap.h" #include "Message.h" #include "Text.h" + #include "Loading Screen.h" double rStart, rEnd; double rActual; @@ -63,10 +64,31 @@ void CreateLoadingScreenProgressBar(BOOLEAN resetLoadScreenHint) // CreateProgressBar(0, 259 + ((SCREEN_WIDTH - 1024) / 2), 683 + ((SCREEN_HEIGHT - 768) / 2), 767 + ((SCREEN_WIDTH - 1024) / 2), 708 + ((SCREEN_HEIGHT - 768) / 2)); // } //} - - CreateProgressBar(0, SCREEN_WIDTH*162/640, SCREEN_HEIGHT*427/480, SCREEN_WIDTH*480/640, SCREEN_HEIGHT*443/480); + FLOAT fScreenAspectRatio = (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT; + if (gGameExternalOptions.ubLoadscreenStretchMode == 1 || + (gGameExternalOptions.ubLoadscreenStretchMode == 2 && fLoadingScreenAspectRatio > fScreenAspectRatio)) + { + // match height, preserve aspect ratioernalOptions.ubLoadscreenStretchMode == 2 && fLoadingScreenAspectRatio > fScreenAspectRatio)) + INT32 iCalculatedWidth = (INT32)(SCREEN_HEIGHT * fLoadingScreenAspectRatio + 0.5f); + + UINT16 usLeft = (UINT16)((SCREEN_WIDTH - iCalculatedWidth) / 2 + (iCalculatedWidth * 162.0f / 640.0f) + 0.5f); + UINT16 usTop = (UINT16)((SCREEN_HEIGHT * 427.0f / 480.0f) + 0.5f); + UINT16 usRight = (UINT16)((SCREEN_WIDTH - iCalculatedWidth) / 2 + (iCalculatedWidth * 478.0f / 640.0f) + 0.5f); + UINT16 usBottom = (UINT16)((SCREEN_HEIGHT * 443.0f / 480.0f) + 0.5f); + + CreateProgressBar(0, usLeft, usTop, usRight, usBottom); + } + else + { + UINT16 usLeft = (UINT16)((SCREEN_WIDTH * 162.0f / 640.0f) + 0.5f); + UINT16 usTop = (UINT16)((SCREEN_HEIGHT * 427.0f / 480.0f) + 0.5f); + UINT16 usRight = (UINT16)((SCREEN_WIDTH * 478.0f / 640.0f) + 0.5f); + UINT16 usBottom = (UINT16)((SCREEN_HEIGHT * 443.0f / 480.0f) + 0.5f); + + CreateProgressBar(0, usLeft, usTop, usRight, usBottom); + } SetProgressBarUseBorder(0, FALSE ); } @@ -334,6 +356,8 @@ void SetRelativeStartAndEndPercentage( UINT8 ubID, UINT16 uiRelStartPerc, UINT16 pCurr->rStart = (double)uiRelStartPerc*0.01f; pCurr->rEnd = (double)uiRelEndPerc*0.01f; + UINT8 yTextOffset = (UINT8)(3.0f * SCREEN_HEIGHT / 480.0f + 0.5f); + //Render the entire panel now, as it doesn't need update during the normal rendering if( pCurr->fPanel ) { @@ -352,7 +376,7 @@ void SetRelativeStartAndEndPercentage( UINT8 ubID, UINT16 uiRelStartPerc, UINT16 usStartX = pCurr->usPanelLeft + // left position (pCurr->usPanelRight - pCurr->usPanelLeft)/2 - // + half width StringPixLength( pCurr->swzTitle, pCurr->usTitleFont ) / 2; // - half string width - usStartY = pCurr->usPanelTop + 3; + usStartY = pCurr->usPanelTop + yTextOffset; SetFont( pCurr->usTitleFont ); SetFontForeground( pCurr->ubTitleFontForeColor ); SetFontShadow( pCurr->ubTitleFontShadowColor ); @@ -370,21 +394,21 @@ void SetRelativeStartAndEndPercentage( UINT8 ubID, UINT16 uiRelStartPerc, UINT16 { UINT16 usFontHeight = GetFontHeight( pCurr->usMsgFont ); - RestoreExternBackgroundRect( pCurr->usBarLeft, pCurr->usBarBottom, (INT16)(pCurr->usBarRight-pCurr->usBarLeft), (INT16)(usFontHeight + 3) ); + RestoreExternBackgroundRect( pCurr->usBarLeft, pCurr->usBarBottom, (INT16)(pCurr->usBarRight-pCurr->usBarLeft), (INT16)(usFontHeight + yTextOffset) ); } SetFont( pCurr->usMsgFont ); SetFontForeground( pCurr->ubMsgFontForeColor ); SetFontShadow( pCurr->ubMsgFontShadowColor ); SetFontBackground( 0 ); - mprintf( pCurr->usBarLeft, pCurr->usBarBottom + 3, str ); + mprintf( pCurr->usBarLeft, pCurr->usBarBottom + yTextOffset, str ); } } // Flugente: loadscreen hints if (gGameExternalOptions.gfUseLoadScreenHints && usCurrentLoadScreenHint ) { - ShowLoadScreenHintInLoadScreen(pCurr->usBarBottom + 3 - 100); + ShowLoadScreenHintInLoadScreen(pCurr->usBarBottom + yTextOffset - 100); } } @@ -408,25 +432,24 @@ void RenderProgressBar( UINT8 ubID, UINT32 uiPercentage ) if( pCurr ) { - rActual = pCurr->rStart+(pCurr->rEnd-pCurr->rStart)*uiPercentage*0.01; + rActual = pCurr->rStart + (pCurr->rEnd - pCurr->rStart) * uiPercentage * 0.01; - if( fabs(rActual - pCurr->rLastActual) < 0.01 ) + pCurr->rLastActual = (DOUBLE)((INT32)(std::round(rActual * 100)) * 0.01); + + end = (INT32)(pCurr->usBarLeft + std::round(rActual * (pCurr->usBarRight - pCurr->usBarLeft))); + if (end < pCurr->usBarLeft) { - return; + end = pCurr->usBarLeft; } - - pCurr->rLastActual = ( DOUBLE )( ( INT32)( rActual * 100 ) * 0.01 ); - - end = (INT32)(pCurr->usBarLeft+2.0+rActual*(pCurr->usBarRight-pCurr->usBarLeft-4)); - if( end < pCurr->usBarLeft+2 || end > pCurr->usBarRight-2 ) + else if (end > pCurr->usBarRight) { - return; + end = pCurr->usBarRight; } if( !pCurr->fDrawBorder ) { ColorFillVideoSurfaceArea( pCurr->uiFrameBuffer, //FRAME_BUFFER, pCurr->usBarLeft, pCurr->usBarTop, end, pCurr->usBarBottom, - Get16BPPColor(FROMRGB( pCurr->ubColorFillRed, pCurr->ubColorFillGreen, pCurr->ubColorFillBlue )) ); + Get16BPPColor(FROMRGB(pCurr->ubColorFillRed, pCurr->ubColorFillGreen, pCurr->ubColorFillBlue))); //if( pCurr->usBarRight > gusLeftmostShaded ) //{ // ShadowVideoSurfaceRect( FRAME_BUFFER, gusLeftmostShaded+1, pCurr->usBarTop, end, pCurr->usBarBottom ); @@ -493,7 +516,7 @@ void SetProgressBarTextDisplayFlag( UINT8 ubID, BOOLEAN fDisplayText, BOOLEAN fU //if we are to use the save buffer, blit the portion of the screen to the save buffer if( fSaveScreenToFrameBuffer ) { - UINT16 usFontHeight = GetFontHeight( pCurr->usMsgFont )+3; + UINT16 usFontHeight = GetFontHeight(pCurr->usMsgFont) + (UINT8)(3.0f * SCREEN_HEIGHT / 480.f + 0.5f); //blit everything to the save buffer ( cause the save buffer can bleed through ) BlitBufferToBuffer(guiRENDERBUFFER, guiSAVEBUFFER, pCurr->usBarLeft, pCurr->usBarBottom, (UINT16)(pCurr->usBarRight-pCurr->usBarLeft), usFontHeight ); From 2060022e944e6171abd8937cc60ee19cf184af2c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Sun, 20 Aug 2023 19:52:34 +0200 Subject: [PATCH 04/24] Battle Panel Multi Resolution (#203) * Battle panel interface with support for multiple resolutions and scrollable list * Support for localized versions of battle panel * Removed commented out code * Fixed wrong copypasta in German version --- Strategic/Auto Resolve.cpp | 4 +- Strategic/Map Screen Interface Bottom.cpp | 2 +- Strategic/PreBattle Interface.cpp | 324 +++++++++++++--------- Strategic/PreBattle Interface.h | 1 + Strategic/mapscreen.cpp | 38 ++- Utils/Multi Language Graphic Utils.cpp | 36 +++ Utils/Multi Language Graphic Utils.h | 3 + 7 files changed, 268 insertions(+), 140 deletions(-) diff --git a/Strategic/Auto Resolve.cpp b/Strategic/Auto Resolve.cpp index 04fa2d561..2029541ee 100644 --- a/Strategic/Auto Resolve.cpp +++ b/Strategic/Auto Resolve.cpp @@ -681,7 +681,7 @@ UINT32 AutoResolveScreenHandle() SGPRect ClipRect; gpAR->fEnteringAutoResolve = FALSE; //Take the framebuffer, shade it, and save it to the SAVEBUFFER. - ClipRect.iLeft = 0 + xResOffset; + ClipRect.iLeft = 0; ClipRect.iTop = 0; /*ClipRect.iRight = 640; ClipRect.iBottom = 480;*/ @@ -692,7 +692,7 @@ UINT32 AutoResolveScreenHandle() Blt16BPPBufferShadowRect( (UINT16*)pDestBuf, uiDestPitchBYTES, &ClipRect ); UnLockVideoSurface( FRAME_BUFFER ); //BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 640, 480 ); - BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0 + xResOffset, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); + BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); KillPreBattleInterface(); CalculateAutoResolveInfo(); CalculateSoldierCells( FALSE ); diff --git a/Strategic/Map Screen Interface Bottom.cpp b/Strategic/Map Screen Interface Bottom.cpp index 0c5530ab3..1cb965938 100644 --- a/Strategic/Map Screen Interface Bottom.cpp +++ b/Strategic/Map Screen Interface Bottom.cpp @@ -1936,7 +1936,7 @@ BOOLEAN AllowedToExitFromMapscreenTo( INT8 bExitToWhere ) } // battle about to occur? - if( ( fDisableDueToBattleRoster ) || ( fDisableMapInterfaceDueToBattle ) ) + if( ( fDisableDueToBattleRoster ) || ( fDisableMapInterfaceDueToBattle ) || ( gfPreBattleInterfaceActive )) { return( FALSE ); } diff --git a/Strategic/PreBattle Interface.cpp b/Strategic/PreBattle Interface.cpp index f05cf42ed..a26c7ab80 100644 --- a/Strategic/PreBattle Interface.cpp +++ b/Strategic/PreBattle Interface.cpp @@ -48,6 +48,7 @@ #include "militiasquads.h" // added by Flugente #include "SkillCheck.h" // added by Flugente #include "Strategic Transport Groups.h" + #include "Utilities.h" #ifdef JA2UB #include "ub_config.h" @@ -99,13 +100,23 @@ enum //GraphicIDs for the panel #define ROW_HEIGHT 10 //The start of the black space #define TOP_Y 113 +#define TOP_Y_TEXT_BUFFER 1 //The end of the black space -//#define BOTTOM_Y (349+(OUR_TEAM_SIZE_NO_VEHICLE-18)*ROW_HEIGHT) -#define BOTTOM_Y 349 +#define BOTTOM_HEIGHT 8 //The internal height of the uninvolved panel -#define INTERNAL_HEIGHT 27 -//The actual height of the uninvolved panel -#define ACTUAL_HEIGHT 24 +#define UNINVOLVED_RELEVANT_HEIGHT 28 +#define UNINVOLVED_OFFSET_HEIGHT 7 + +#define PREBATTLE_INTERFACE_WIDTH 261 +INT32 iPrebattleInterfaceHeight = 360; +UINT16 xOffset; +UINT16 yOffset; +UINT16 blanketStartX; +UINT16 blanketStartY; + +INT16 bListOffset = 0; +UINT16 ubDesiredListHeight = 0; +UINT16 ubAllowedListHeight = 0; BOOLEAN gfDisplayPotentialRetreatPaths = FALSE; UINT16 gusRetreatButtonLeft, gusRetreatButtonTop, gusRetreatButtonRight, gusRetreatButtonBottom; @@ -300,6 +311,8 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) gAmbushRadiusModifier = 0.0f; + bListOffset = 0; + // ARM: Feb01/98 - Cancel out of mapscreen movement plotting if PBI subscreen is coming up if ( ( GetSelectedDestChar() != -1) || fPlotForHelicopter || fPlotForMilitia ) { @@ -481,32 +494,69 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ) fMapScreenBottomDirty = TRUE; ChangeSelectedMapSector( gubPBSectorX, gubPBSectorY, gubPBSectorZ ); - // Headrock: Added FALSE argument, We might need TRUE but not sure. Will need to initiate battle :) - RenderMapScreenInterfaceBottom( FALSE ); if( !fShowTeamFlag ) { ToggleShowTeamsMode(); } - //Define the blanket region to cover all of the other regions used underneath the panel. - MSYS_DefineRegion( &PBInterfaceBlanket, 0 + xResOffset, 0 + yResOffset, 261 + xResOffset, 359 + yResOffset, MSYS_PRIORITY_HIGHEST - 5, 0, 0, 0 ); - //Create the panel VObjectDesc.fCreateFlags = VOBJECT_CREATE_FROMFILE; - GetMLGFilename( VObjectDesc.ImageFile, MLG_PREBATTLEPANEL ); + + // anv: prebattle interface per vertical resolution + if (isWidescreenUI()) + { + GetMLGFilename(VObjectDesc.ImageFile, MLG_PREBATTLEPANEL_1280x720); + iPrebattleInterfaceHeight = 600; + xOffset = xResOffset + 15; + yOffset = 0; + blanketStartX = 0; + blanketStartY = 0; + } + else if (iResolution >= _640x480 && iResolution < _800x600) + { + GetMLGFilename(VObjectDesc.ImageFile, MLG_PREBATTLEPANEL); + iPrebattleInterfaceHeight = 360; + xOffset = xResOffset; + yOffset = yResOffset; + blanketStartX = yOffset; + blanketStartY = yOffset; + } + else if (iResolution < _1024x768) + { + GetMLGFilename(VObjectDesc.ImageFile, MLG_PREBATTLEPANEL_800x600); + iPrebattleInterfaceHeight = 478; + xOffset = xResOffset; + yOffset = yResOffset; + blanketStartX = yOffset; + blanketStartY = yOffset; + } + else + { + GetMLGFilename(VObjectDesc.ImageFile, MLG_PREBATTLEPANEL_1024x768); + iPrebattleInterfaceHeight = 647; + xOffset = xResOffset; + yOffset = yResOffset; + blanketStartX = yOffset; + blanketStartY = yOffset; + } + ubAllowedListHeight = iPrebattleInterfaceHeight - TOP_Y - BOTTOM_HEIGHT; + if( !AddVideoObject( &VObjectDesc, &uiInterfaceImages ) ) AssertMsg( 0, "Failed to load interface\\PreBattlePanel.sti" ); + //Define the blanket region to cover all of the other regions used underneath the panel. + MSYS_DefineRegion( &PBInterfaceBlanket, blanketStartX, blanketStartY, PREBATTLE_INTERFACE_WIDTH + xOffset, iPrebattleInterfaceHeight + yOffset, MSYS_PRIORITY_HIGHEST, 0, 0, 0 ); + //Create the 3 buttons iPBButtonImage[0] = LoadButtonImage( "INTERFACE\\PreBattleButton.sti", -1, 0, -1, 1, -1 ); if( iPBButtonImage[ 0 ] == -1 ) AssertMsg( 0, "Failed to load interface\\PreBattleButton.sti" ); iPBButtonImage[1] = UseLoadedButtonImage( iPBButtonImage[ 0 ], -1, 0, -1, 1, -1 ); iPBButtonImage[2] = UseLoadedButtonImage( iPBButtonImage[ 0 ], -1, 0, -1, 1, -1 ); - iPBButton[0] = QuickCreateButton( iPBButtonImage[0], 27 + xResOffset, 54 + yResOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST - 2, DEFAULT_MOVE_CALLBACK, AutoResolveBattleCallback ); - iPBButton[1] = QuickCreateButton( iPBButtonImage[1], 98 + xResOffset, 54 + yResOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST - 2, DEFAULT_MOVE_CALLBACK, GoToSectorCallback ); - iPBButton[2] = QuickCreateButton( iPBButtonImage[2], 169 + xResOffset, 54 + yResOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST - 2, DEFAULT_MOVE_CALLBACK, RetreatMercsCallback ); + iPBButton[0] = QuickCreateButton( iPBButtonImage[0], 27 + xOffset, 54 + yOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST, DEFAULT_MOVE_CALLBACK, AutoResolveBattleCallback ); + iPBButton[1] = QuickCreateButton( iPBButtonImage[1], 98 + xOffset, 54 + yOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST, DEFAULT_MOVE_CALLBACK, GoToSectorCallback ); + iPBButton[2] = QuickCreateButton( iPBButtonImage[2], 169 + xOffset, 54 + yOffset, BUTTON_NO_TOGGLE, MSYS_PRIORITY_HIGHEST, DEFAULT_MOVE_CALLBACK, RetreatMercsCallback ); SpecifyGeneralButtonTextAttributes( iPBButton[0], gpStrategicString[ STR_PB_AUTORESOLVE_BTN ], BLOCKFONT, FONT_BEIGE, 141 ); SpecifyGeneralButtonTextAttributes( iPBButton[1], gpStrategicString[ STR_PB_GOTOSECTOR_BTN ], BLOCKFONT, FONT_BEIGE, 141 ); @@ -1050,7 +1100,7 @@ void DoTransitionFromMapscreenToPreBattleInterface() INT32 iPercentage, iFactor; UINT32 uiTimeRange; INT16 sStartLeft, sEndLeft, sStartTop, sEndTop; - INT32 iLeft, iTop, iWidth, iHeight; + INT32 iLeft, iTop, iWidth; BOOLEAN fEnterAutoResolveMode = FALSE; if( !gfExtraBuffer ) @@ -1058,12 +1108,11 @@ void DoTransitionFromMapscreenToPreBattleInterface() PauseTime( FALSE ); - PBIRect.iLeft = 0 + xResOffset; - PBIRect.iTop = 0 + yResOffset; - PBIRect.iRight = 261 + xResOffset; - PBIRect.iBottom = 359 + yResOffset; - iWidth = 261; - iHeight = 359; + PBIRect.iLeft = 0 + xOffset; + PBIRect.iTop = 0 + yOffset; + PBIRect.iRight = PREBATTLE_INTERFACE_WIDTH + xOffset; + PBIRect.iBottom = iPrebattleInterfaceHeight + yOffset; + iWidth = PREBATTLE_INTERFACE_WIDTH; uiTimeRange = 1000; iPercentage = 0; @@ -1072,8 +1121,8 @@ void DoTransitionFromMapscreenToPreBattleInterface() GetScreenXYFromMapXY( gubPBSectorX, gubPBSectorY, &sStartLeft, &sStartTop ); sStartLeft += UI_MAP.GridSize.iX / 2; sStartTop += UI_MAP.GridSize.iY / 2; - sEndLeft = 131 + xResOffset; - sEndTop = 180 + yResOffset; + sEndLeft = PBIRect.iLeft; + sEndTop = PBIRect.iTop; //save the mapscreen buffer BlitBufferToBuffer( FRAME_BUFFER, guiEXTRABUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); @@ -1095,17 +1144,23 @@ void DoTransitionFromMapscreenToPreBattleInterface() gfEnterAutoResolveMode = TRUE; } - BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, 27 + xResOffset, 54 + yResOffset, 209, 32 ); + BlitBufferToBuffer( guiSAVEBUFFER, FRAME_BUFFER, 27 + xOffset, 54 + yOffset, 209, 32 ); RenderButtons(); - BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 27 + xResOffset, 54 + yResOffset, 209, 32 ); + BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 27 + xOffset, 54 + yOffset, 209, 32 ); gfRenderPBInterface = TRUE; //hide the prebattle interface - BlitBufferToBuffer( guiEXTRABUFFER, FRAME_BUFFER, 0 + xResOffset, 0 + yResOffset, 261 + xResOffset, 359 + yResOffset ); + BlitBufferToBuffer( guiEXTRABUFFER, FRAME_BUFFER, 0 + xOffset, 0 + yOffset, PREBATTLE_INTERFACE_WIDTH + xOffset, iPrebattleInterfaceHeight + yOffset ); PlayJA2SampleFromFile( "SOUNDS\\Laptop power up (8-11).wav", RATE_11025, HIGHVOLUME, 1, MIDDLEPAN ); InvalidateScreen(); RefreshScreen( NULL ); + SGPRect PrevRect; + PrevRect.iLeft = sStartLeft; + PrevRect.iRight = sStartLeft + 1; + PrevRect.iTop = sStartTop; + PrevRect.iBottom = sStartTop + 1; + while( iPercentage < 100 ) { uiCurrTime = GetJA2Clock(); @@ -1117,19 +1172,12 @@ void DoTransitionFromMapscreenToPreBattleInterface() if( iPercentage < 50 ) iPercentage = (UINT32)(iPercentage + iPercentage * iFactor * 0.01 + 0.5); else - iPercentage = (UINT32)(iPercentage + (100-iPercentage) * iFactor * 0.01 + 0.05); + iPercentage = (UINT32)(iPercentage + (100 - iPercentage) * iFactor * 0.01 + 0.05); - //Calculate the center point. - iLeft = sStartLeft - (sStartLeft-sEndLeft+1) * iPercentage / 100; - if( sStartTop > sEndTop ) - iTop = sStartTop - (sStartTop-sEndTop+1) * iPercentage / 100; - else - iTop = sStartTop + (sEndTop-sStartTop+1) * iPercentage / 100; - - DstRect.iLeft = iLeft - iWidth * iPercentage / 200; + DstRect.iLeft = sStartLeft + (sEndLeft - sStartLeft) * iPercentage / 100; DstRect.iRight = DstRect.iLeft + max( iWidth * iPercentage / 100, 1 ); - DstRect.iTop = iTop - iHeight * iPercentage / 200; - DstRect.iBottom = DstRect.iTop + max( iHeight * iPercentage / 100, 1 ); + DstRect.iTop = sStartTop + (sEndTop - sStartTop) * iPercentage / 100; + DstRect.iBottom = DstRect.iTop + max(iPrebattleInterfaceHeight * iPercentage / 100, 1); BltStretchVideoSurface( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, 0, &PBIRect, &DstRect ); @@ -1137,14 +1185,35 @@ void DoTransitionFromMapscreenToPreBattleInterface() RefreshScreen( NULL ); //Restore the previous rect. - BlitBufferToBuffer( guiEXTRABUFFER, FRAME_BUFFER, (UINT16)DstRect.iLeft, (UINT16)DstRect.iTop, - (UINT16)(DstRect.iRight-DstRect.iLeft+1), (UINT16)(DstRect.iBottom-DstRect.iTop+1) ); + BlitBufferToBuffer(guiEXTRABUFFER, FRAME_BUFFER, (UINT16)PrevRect.iLeft, (UINT16)PrevRect.iTop, + (UINT16)PrevRect.iRight, (UINT16)PrevRect.iBottom); + + PrevRect.iLeft = DstRect.iLeft; + PrevRect.iRight = DstRect.iRight; + PrevRect.iTop = DstRect.iTop; + PrevRect.iBottom = DstRect.iBottom; } BlitBufferToBuffer( FRAME_BUFFER, guiSAVEBUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT ); gfZoomDone = TRUE; } +void ScrollPreBattleInterface( BOOLEAN fUp ) +{ + if ( ubDesiredListHeight <= ubAllowedListHeight ) + return; + + if ( fUp ) + { + bListOffset = max( 0, bListOffset - ubAllowedListHeight ); + } + else + { + bListOffset = min( bListOffset + ubAllowedListHeight, ubDesiredListHeight - ubAllowedListHeight ); + } + gfRenderPBInterface = TRUE; +} + void KillPreBattleInterface() { if( !gfPreBattleInterfaceActive ) @@ -1184,7 +1253,7 @@ void KillPreBattleInterface() //Enable the options button when the auto resolve screen comes up EnableDisAbleMapScreenOptionsButton( TRUE ); - ColorFillVideoSurfaceArea( guiSAVEBUFFER, 0, 0, 261 + xResOffset, 359 + yResOffset, 0 ); + ColorFillVideoSurfaceArea( guiSAVEBUFFER, 0, 0, PREBATTLE_INTERFACE_WIDTH + xOffset, iPrebattleInterfaceHeight + yOffset, 0 ); EnableTeamInfoPanels(); if( ButtonList[ giMapContractButton ] ) @@ -1277,8 +1346,8 @@ void RenderPBHeader( INT32 *piX, INT32 *piWidth) } width = StringPixLength( str, FONT10ARIALBOLD ); x = 130 - width / 2; - mprintf( x + xResOffset, 4 + yResOffset, str ); - InvalidateRegion( 0, 0, 231 + xResOffset, 12 + yResOffset ); + mprintf( x + xOffset, 4 + yOffset, str ); + InvalidateRegion( 0, 0, 231 + xOffset, 12 + yOffset ); *piX = x; *piWidth = width; } @@ -1293,10 +1362,15 @@ void RenderPreBattleInterface() UINT8 ubHPPercent, ubBPPercent; BOOLEAN fMouseInRetreatButtonArea; UINT8 ubJunk; + SGPRect ClipRect; + UINT16 ubDesiredParticipantsListHeight = 0; + UINT16 ubDesiredUninvolvedListHeight = 0; + UINT16 ubUninvolvedStartY = 0; + //PLAYERGROUP *pPlayer; // Make the background black! - //ColorFillVideoSurfaceArea( guiSAVEBUFFER, 0, 0, 261 + xResOffset, SCREEN_HEIGHT - 120, 0 ); + //ColorFillVideoSurfaceArea( guiSAVEBUFFER, 0, 0, 261 + xOffset, SCREEN_HEIGHT - 120, 0 ); //This code determines if the cursor is inside the rectangle consisting of the //retreat button. If it is inside, then we set up the variables so that the retreat @@ -1337,33 +1411,21 @@ void RenderPreBattleInterface() gfRenderPBInterface = FALSE; GetVideoObject( &hVObject, uiInterfaceImages ); //main panel - BltVideoObject( guiSAVEBUFFER, hVObject, MAINPANEL, xResOffset, yResOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + BltVideoObject( guiSAVEBUFFER, hVObject, MAINPANEL, xOffset, yOffset, VO_BLT_SRCTRANSPARENCY, NULL ); //main title RenderPBHeader( &x, &width ); //now draw the title bars up to the text. for( i = x - 12; i > 20; i -= 10 ) { - BltVideoObject( guiSAVEBUFFER, hVObject, TITLE_BAR_PIECE, i + xResOffset, 6 + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + BltVideoObject( guiSAVEBUFFER, hVObject, TITLE_BAR_PIECE, i + xOffset, 6 + yOffset, VO_BLT_SRCTRANSPARENCY, NULL ); } for( i = x + width + 2; i < 231; i += 10 ) { - BltVideoObject( guiSAVEBUFFER, hVObject, TITLE_BAR_PIECE, i + xResOffset, 6 + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + BltVideoObject( guiSAVEBUFFER, hVObject, TITLE_BAR_PIECE, i + xOffset, 6 + yOffset, VO_BLT_SRCTRANSPARENCY, NULL ); } - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_LINE, 0 + xResOffset, BOTTOM_Y + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL); - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_LINE, 0 + xResOffset, BOTTOM_Y + yResOffset + 10, VO_BLT_SRCTRANSPARENCY, NULL); - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_LINE, 0 + xResOffset, BOTTOM_Y + yResOffset + 20, VO_BLT_SRCTRANSPARENCY, NULL); - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_LINE, 0 + xResOffset, BOTTOM_Y + yResOffset + 30, VO_BLT_SRCTRANSPARENCY, NULL); - //Draw the bottom edges - for (i = 0; i < max(guiNumUninvolved, 1); i++) - { - y = BOTTOM_Y + ROW_HEIGHT * (i + 1) + 30; - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_LINE, 0 + xResOffset, y + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL); - } - BltVideoObject(guiSAVEBUFFER, hVObject, UNINVOLVED_HEADER, 8 + xResOffset, BOTTOM_Y + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL); - BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_END, 0 + xResOffset, BOTTOM_Y + yResOffset + 35 + ROW_HEIGHT * max(guiNumUninvolved, 1), VO_BLT_SRCTRANSPARENCY, NULL); - + // header SetFont( BLOCKFONT ); SetFontForeground( FONT_BEIGE ); swprintf( str, gpStrategicString[ STR_PB_LOCATION ] ); @@ -1373,7 +1435,7 @@ void RenderPreBattleInterface() SetFont( BLOCKFONTNARROW ); width = StringPixLength( str, BLOCKFONTNARROW ); } - mprintf( 65 - width + xResOffset , 17 + yResOffset, str ); + mprintf( 65 - width + xOffset , 17 + yOffset, str ); SetFont( BLOCKFONT ); if( GetEnemyEncounterCode() == CREATURE_ATTACK_CODE ) @@ -1405,7 +1467,7 @@ void RenderPreBattleInterface() SetFont( BLOCKFONTNARROW ); width = StringPixLength( str, BLOCKFONTNARROW ); } - mprintf( 54 + xResOffset - width , 38 + yResOffset, str ); + mprintf( 54 + xOffset - width , 38 + yOffset, str ); SetFont( BLOCKFONT ); swprintf( str, gpStrategicString[ STR_PB_MERCS ] ); @@ -1415,7 +1477,7 @@ void RenderPreBattleInterface() SetFont( BLOCKFONTNARROW ); width = StringPixLength( str, BLOCKFONTNARROW ); } - mprintf( 139 + xResOffset - width , 38 + yResOffset, str ); + mprintf( 139 + xOffset - width , 38 + yOffset, str ); SetFont( BLOCKFONT ); swprintf( str, gpStrategicString[ STR_PB_MILITIA ] ); @@ -1425,29 +1487,53 @@ void RenderPreBattleInterface() SetFont( BLOCKFONTNARROW ); width = StringPixLength( str, BLOCKFONTNARROW ); } - mprintf( 224 + xResOffset - width , 38 + yResOffset, str ); + mprintf( 224 + xOffset - width , 38 + yOffset, str ); - //Draw the bottom columns - for( i = 0; i < (INT32)max( guiNumUninvolved, 1 ); i++ ) + ubDesiredParticipantsListHeight = guiNumInvolved * ROW_HEIGHT; + ubDesiredUninvolvedListHeight = UNINVOLVED_RELEVANT_HEIGHT + max(guiNumUninvolved, 1) * ROW_HEIGHT; + + ubDesiredListHeight = ubDesiredParticipantsListHeight + ubDesiredUninvolvedListHeight; + + if (ubDesiredListHeight >= ubAllowedListHeight) { - y = BOTTOM_Y + ROW_HEIGHT * (i+1) + 1 + ACTUAL_HEIGHT; - BltVideoObject( guiSAVEBUFFER, hVObject, BOTTOM_COLUMN, 161 + xResOffset, y + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + ubUninvolvedStartY = ubDesiredParticipantsListHeight; + } + else + { + ubUninvolvedStartY = ubAllowedListHeight - ubDesiredUninvolvedListHeight; } - // WDS - make number of mercenaries, etc. be configurable - for( i = 0; i < (INT32)(25/*3+ OUR_TEAM_SIZE_NO_VEHICLE - max( guiNumUninvolved, 1 )*/); i++ ) + ClipRect.iLeft = xOffset; + ClipRect.iTop = yOffset + TOP_Y; + ClipRect.iRight = xOffset + PREBATTLE_INTERFACE_WIDTH; + ClipRect.iBottom = yOffset + TOP_Y + ubAllowedListHeight;// + TOP_Y_BUFFER; + SetClippingRect(&ClipRect); + + // Draw the top columns + // Draw from the top to uninvolved header + for ( y = TOP_Y - bListOffset; y < TOP_Y - bListOffset + ubUninvolvedStartY; y += ROW_HEIGHT ) { - y = TOP_Y + ROW_HEIGHT * i; - BltVideoObject( guiSAVEBUFFER, hVObject, TOP_COLUMN, 186 + xResOffset, y + yResOffset, VO_BLT_SRCTRANSPARENCY, NULL ); + BltVideoObject(guiSAVEBUFFER, hVObject, TOP_COLUMN, 186 + xOffset, y + yOffset, VO_BLT_CLIP | VO_BLT_SRCTRANSPARENCY, NULL); } + // Draw extra empty participants rows to close off bottom of the content area + for ( y = TOP_Y - bListOffset + ubUninvolvedStartY + ROW_HEIGHT; y < TOP_Y - bListOffset + ubUninvolvedStartY + ubDesiredUninvolvedListHeight; y += ROW_HEIGHT ) + { + BltVideoObject(guiSAVEBUFFER, hVObject, BOTTOM_COLUMN, 161 + xOffset, y + yOffset, VO_BLT_CLIP | VO_BLT_SRCTRANSPARENCY, NULL); + } + + // Draw uninvolved header + BltVideoObject( guiSAVEBUFFER, hVObject, UNINVOLVED_HEADER, 8 + xOffset, yOffset + TOP_Y + ubUninvolvedStartY - UNINVOLVED_OFFSET_HEIGHT - bListOffset, VO_BLT_CLIP | VO_BLT_SRCTRANSPARENCY, NULL ); + + RestoreClipRegionToFullScreen(); + //location SetFont( FONT10ARIAL ); SetFontForeground( FONT_YELLOW ); SetFontShadow( FONT_NEARBLACK ); GetSectorIDString( gubPBSectorX, gubPBSectorY, gubPBSectorZ, pSectorName, TRUE ); - mprintf( 70 + xResOffset, 17 + yResOffset, L"%s %s", gpStrategicString[ STR_PB_SECTOR ], pSectorName ); + mprintf( 70 + xOffset, 17 + yOffset, L"%s %s", gpStrategicString[STR_PB_SECTOR], pSectorName ); //enemy SetFont( FONT14ARIAL ); @@ -1472,58 +1558,55 @@ void RenderPreBattleInterface() } x = 57 + (27 - StringPixLength( str, FONT14ARIAL )) / 2; y = 36; - mprintf( x + xResOffset, y + yResOffset, str ); + mprintf( x + xOffset, y + yOffset, str ); //player swprintf( str, L"%d", guiNumInvolved ); x = 142 + (27 - StringPixLength( str, FONT14ARIAL )) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + mprintf( x + xOffset, y + yOffset, str ); //militia swprintf( str, L"%d", NumNonPlayerTeamMembersInSector( gubPBSectorX, gubPBSectorY, MILITIA_TEAM ) ); x = 227 + (27 - StringPixLength( str, FONT14ARIAL )) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + mprintf( x + xOffset, y + yOffset, str ); SetFontShadow( FONT_NEARBLACK ); SetFont( BLOCKFONT2 ); SetFontForeground( FONT_YELLOW ); + SetFontDestBuffer( guiSAVEBUFFER, xOffset, yOffset + TOP_Y, + xOffset + PREBATTLE_INTERFACE_WIDTH, yOffset + TOP_Y + ubAllowedListHeight, FALSE ); + //print out the participants of the battle. // | NAME | ASSIGN | COND | HP | BP | line = 0; - y = TOP_Y + 1; - for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) + y = TOP_Y + TOP_Y_TEXT_BUFFER - bListOffset; + for( i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; i++) { - if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) ) + if( MercPtrs[i]->bActive && MercPtrs[i]->stats.bLife && !(MercPtrs[i]->flags.uiStatusFlags & SOLDIER_VEHICLE) ) { - if ( PlayerMercInvolvedInThisCombat( MercPtrs[ i ] ) ) - { //involved - if( line == giHilitedInvolved ) - SetFontForeground( FONT_WHITE ); - else - SetFontForeground( FONT_YELLOW ); + if( PlayerMercInvolvedInThisCombat( MercPtrs[ i ] ) ) + { //NAME wcscpy( str, MercPtrs[ i ]->name ); - x = 17 + (52-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset , y + yResOffset, str ); + x = 17 + (52 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //ASSIGN GetMapscreenMercAssignmentString( MercPtrs[ i ], str ); - x = 72 + (54-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 72 + (54 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //COND GetSoldierConditionInfo( MercPtrs[ i ], str, &ubHPPercent, &ubBPPercent ); - x = 129 + (58-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 129 + (58 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //HP swprintf( str, L"%d%%", ubHPPercent ); - x = 189 + (25-StringPixLength( str, BLOCKFONT2)) / 2; + x = 189 + (25 - StringPixLength(str, BLOCKFONT2)) / 2; wcscat( str, sSpecialCharacters[0] ); - mprintf( x + xResOffset, y + yResOffset, str ); + mprintf( x + xOffset, y + yOffset, str ); //BP swprintf( str, L"%d%%", ubBPPercent ); x = 217 + (25-StringPixLength( str, BLOCKFONT2)) / 2; wcscat( str, sSpecialCharacters[0] ); - mprintf( x + xResOffset, y + yResOffset, str ); - - line++; + mprintf( x + xOffset, y + yOffset, str ); y += ROW_HEIGHT; } } @@ -1533,51 +1616,44 @@ void RenderPreBattleInterface() // | NAME | ASSIGN | LOC | DEST | DEP | if( !guiNumUninvolved ) { - SetFontForeground( FONT_YELLOW ); wcscpy( str, gpStrategicString[ STR_PB_NONE ] ); - x = 17 + (52-StringPixLength( str, BLOCKFONT2)) / 2; - y = BOTTOM_Y + ROW_HEIGHT + 2 + ACTUAL_HEIGHT; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 17 + (52 - StringPixLength( str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, yOffset + TOP_Y + TOP_Y_TEXT_BUFFER + ubUninvolvedStartY + UNINVOLVED_RELEVANT_HEIGHT - bListOffset, str ); } else { pGroup = gpGroupList; - y = BOTTOM_Y + ROW_HEIGHT + 2 + ACTUAL_HEIGHT; - for( i = gTacticalStatus.Team[ OUR_TEAM ].bFirstID; i <= gTacticalStatus.Team[ OUR_TEAM ].bLastID; i++ ) + y = TOP_Y + TOP_Y_TEXT_BUFFER + ubUninvolvedStartY + UNINVOLVED_RELEVANT_HEIGHT - bListOffset; + for( i = gTacticalStatus.Team[OUR_TEAM].bFirstID; i <= gTacticalStatus.Team[OUR_TEAM].bLastID; i++ ) { if( MercPtrs[ i ]->bActive && MercPtrs[ i ]->stats.bLife && !(MercPtrs[ i ]->flags.uiStatusFlags & SOLDIER_VEHICLE) ) { - if ( !PlayerMercInvolvedInThisCombat( MercPtrs[ i ] ) ) + if( !PlayerMercInvolvedInThisCombat(MercPtrs[ i ]) ) { - // uninvolved - if( line == giHilitedUninvolved ) - SetFontForeground( FONT_WHITE ); - else - SetFontForeground( FONT_YELLOW ); //NAME wcscpy( str, MercPtrs[ i ]->name ); - x = 17 + (52-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 17 + (52 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //ASSIGN GetMapscreenMercAssignmentString( MercPtrs[ i ], str ); - x = 72 + (54-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 72 + (54 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //LOC GetMapscreenMercLocationString( MercPtrs[ i ], str ); - x = 128 + (33-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 128 + (33 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); //DEST GetMapscreenMercDestinationString( MercPtrs[ i ], str ); - if( wcslen( str ) > 0 ) + if (wcslen(str) > 0) { - x = 164 + (41-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); + x = 164 + (41 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf( x + xOffset, y + yOffset, str ); } //DEP GetMapscreenMercDepartureString( MercPtrs[ i ], str, &ubJunk ); - x = 208 + (34-StringPixLength( str, BLOCKFONT2)) / 2; - mprintf( x + xResOffset, y + yResOffset, str ); - line++; + x = 208 + (34 - StringPixLength(str, BLOCKFONT2)) / 2; + mprintf(x + xOffset, y + yOffset, str); + y += ROW_HEIGHT; } } @@ -1587,12 +1663,7 @@ void RenderPreBattleInterface() // mark any and ALL pop up boxes as altered MarkAllBoxesAsAltered( ); - if(!gfZoomDone) - RestoreExternBackgroundRect( 0 + xResOffset, 0 + yResOffset, 261 + xResOffset, 359 + yResOffset ); - else if(!guiNumUninvolved) - RestoreExternBackgroundRect( 0 + xResOffset, 0 + yResOffset, 261 + xResOffset, 389 + yResOffset ); - else - RestoreExternBackgroundRect( 0 + xResOffset, 0 + yResOffset, 261 + xResOffset, y + yResOffset ); + RestoreExternBackgroundRect( 0 + xOffset, 0 + yOffset, PREBATTLE_INTERFACE_WIDTH, iPrebattleInterfaceHeight ); // restore font destinanation buffer to the frame buffer SetFontDestBuffer( FRAME_BUFFER, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, FALSE ); @@ -1602,7 +1673,7 @@ void RenderPreBattleInterface() RenderPBHeader( &x, &width ); //the text is important enough to blink. } - //InvalidateRegion( 0, 0, 261, 359 ); + InvalidateRegion( 0, 0, PREBATTLE_INTERFACE_WIDTH, iPrebattleInterfaceHeight ); if( gfEnterAutoResolveMode ) { gfEnterAutoResolveMode = FALSE; @@ -1611,7 +1682,6 @@ void RenderPreBattleInterface() } gfIgnoreAllInput = FALSE; - } void AutoResolveBattleCallback( GUI_BUTTON *btn, INT32 reason ) diff --git a/Strategic/PreBattle Interface.h b/Strategic/PreBattle Interface.h index 78070486e..f0406674b 100644 --- a/Strategic/PreBattle Interface.h +++ b/Strategic/PreBattle Interface.h @@ -7,6 +7,7 @@ void InitPreBattleInterface( GROUP *pBattleGroup, BOOLEAN fPersistantPBI ); void KillPreBattleInterface(); void RenderPreBattleInterface(); +void ScrollPreBattleInterface( BOOLEAN fUp ); extern BOOLEAN gfPreBattleInterfaceActive; extern BOOLEAN gfDisplayPotentialRetreatPaths; diff --git a/Strategic/mapscreen.cpp b/Strategic/mapscreen.cpp index 4d387161c..655d6b50e 100644 --- a/Strategic/mapscreen.cpp +++ b/Strategic/mapscreen.cpp @@ -5708,7 +5708,7 @@ UINT32 MapScreenHandle(void) HandleCharBarRender( ); } - if( (fShowInventoryFlag && !isWidescreenUI()) || fDisableDueToBattleRoster ) + if( ( fShowInventoryFlag || fDisableDueToBattleRoster ) && !isWidescreenUI() ) { for( iCounter = 0; iCounter < MAX_SORT_METHODS; iCounter++ ) { @@ -5824,9 +5824,6 @@ UINT32 MapScreenHandle(void) HandleContractRenewalSequence( ); - // handle dialog - HandleDialogue( ); - // handle display of inventory pop up // HEADROCK HAM 3.5: Externalize! HandleDisplayOfItemPopUpForSector( gGameExternalOptions.ubDefaultArrivalSectorX, gGameExternalOptions.ubDefaultArrivalSectorY, startingZ ); @@ -6065,6 +6062,8 @@ UINT32 MapScreenHandle(void) //InvalidateRegion( 0,0, 640, 480); EndFrameBufferRender( ); + // handle dialog + HandleDialogue(); // if not going anywhere else if ( guiPendingScreen == NO_PENDING_SCREEN ) @@ -7225,14 +7224,29 @@ void GetMapKeyboardInput( UINT32 *puiNewEvent ) break; case PGUP: - // WANNE: Jump to first merc in list - fResetMapCoords = TRUE; - GoToFirstCharacterInList( ); + if (gfPreBattleInterfaceActive) + { + ScrollPreBattleInterface(TRUE); + } + else + { + // WANNE: Jump to first merc in list + fResetMapCoords = TRUE; + GoToFirstCharacterInList(); + } + break; case PGDN: - // WANNE: Jump to last merc in list - fResetMapCoords = TRUE; - GoToLastCharacterInList( ); + if (gfPreBattleInterfaceActive) + { + ScrollPreBattleInterface(FALSE); + } + else + { + // WANNE: Jump to last merc in list + fResetMapCoords = TRUE; + GoToLastCharacterInList(); + } break; case SHIFT_PGUP: @@ -10927,6 +10941,10 @@ void BlitBackgroundToSaveBuffer( void ) ForceButtonUnDirty( giMapContractButton ); ForceButtonUnDirty( giCharInfoButton[ 0 ] ); ForceButtonUnDirty( giCharInfoButton[ 1 ] ); + if (isWidescreenUI()) + { + ForceButtonUnDirty(giMapInvDoneButton); + } RenderPreBattleInterface(); } diff --git a/Utils/Multi Language Graphic Utils.cpp b/Utils/Multi Language Graphic Utils.cpp index 0371e72e2..102c7d3f1 100644 --- a/Utils/Multi Language Graphic Utils.cpp +++ b/Utils/Multi Language Graphic Utils.cpp @@ -76,6 +76,15 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_PREBATTLEPANEL: sprintf( filename, "INTERFACE\\PreBattlePanel.sti" ); return TRUE; + case MLG_PREBATTLEPANEL_800x600: + sprintf(filename, "INTERFACE\\PreBattlePanel_800x600.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1024x768: + sprintf(filename, "INTERFACE\\PreBattlePanel_1024x768.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1280x720: + sprintf(filename, "INTERFACE\\PreBattlePanel_1280x720.sti"); + return TRUE; case MLG_SMALLTITLE: sprintf( filename, "LAPTOP\\SmallTitle.sti" ); return TRUE; @@ -203,6 +212,15 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_PREBATTLEPANEL: sprintf( filename, "GERMAN\\PreBattlePanel_german.sti" ); return TRUE; + case MLG_PREBATTLEPANEL_800x600: + sprintf(filename, "GERMAN\\PreBattlePanel_800x600_german.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1024x768: + sprintf(filename, "GERMAN\\PreBattlePanel_1024x768_german.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1280x720: + sprintf(filename, "GERMAN\\PreBattlePanel_1280x720_german.sti"); + return TRUE; case MLG_SMALLTITLE: sprintf( filename, "GERMAN\\SmallTitle_german.sti" ); return TRUE; @@ -368,6 +386,15 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_PREBATTLEPANEL: sprintf( filename, "%s\\PreBattlePanel_%s.sti", zLanguage, zLanguage ); break; + case MLG_PREBATTLEPANEL_800x600: + sprintf(filename, "%s\\PreBattlePanel_800x600_%s.sti", zLanguage, zLanguage); + break; + case MLG_PREBATTLEPANEL_1024x768: + sprintf(filename, "%s\\PreBattlePanel_1024x768_%s.sti", zLanguage, zLanguage); + break; + case MLG_PREBATTLEPANEL_1280x720: + sprintf(filename, "%s\\PreBattlePanel_1280x720_%s.sti", zLanguage, zLanguage); + break; case MLG_SMALLTITLE: sprintf( filename, "%s\\SmallTitle_%s.sti", zLanguage, zLanguage ); break; @@ -492,6 +519,15 @@ BOOLEAN GetMLGFilename( SGPFILENAME filename, UINT16 usMLGGraphicID ) case MLG_PREBATTLEPANEL: sprintf( filename, "INTERFACE\\PreBattlePanel.sti" ); return TRUE; + case MLG_PREBATTLEPANEL_800x600: + sprintf(filename, "INTERFACE\\PreBattlePanel_800x600.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1024x768: + sprintf(filename, "INTERFACE\\PreBattlePanel_1024x768.sti"); + return TRUE; + case MLG_PREBATTLEPANEL_1280x720: + sprintf(filename, "INTERFACE\\PreBattlePanel_1280x720.sti"); + return TRUE; case MLG_SMALLTITLE: sprintf( filename, "LAPTOP\\SmallTitle.sti" ); return TRUE; diff --git a/Utils/Multi Language Graphic Utils.h b/Utils/Multi Language Graphic Utils.h index 12f74fd59..dcedbcc13 100644 --- a/Utils/Multi Language Graphic Utils.h +++ b/Utils/Multi Language Graphic Utils.h @@ -26,6 +26,9 @@ enum MLG_OPTIONHEADER, //OptionScreenAddOns MLG_ORDERGRID, MLG_PREBATTLEPANEL, + MLG_PREBATTLEPANEL_800x600, + MLG_PREBATTLEPANEL_1024x768, + MLG_PREBATTLEPANEL_1280x720, MLG_SECTORINVENTORY, MLG_SMALLFLORISTSYMBOL, //SmallSymbol MLG_SMALLTITLE, From 1e1d456fd27c9fe70e51f4e2da76347b138a64a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 21 Aug 2023 18:44:02 +0200 Subject: [PATCH 05/24] Prevent deadlocks due to self-healing interactive spots and self-attacking (#205) --- Tactical/Handle Items.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Tactical/Handle Items.cpp b/Tactical/Handle Items.cpp index 9c9b47fca..a865d7031 100644 --- a/Tactical/Handle Items.cpp +++ b/Tactical/Handle Items.cpp @@ -279,7 +279,8 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa { pTargetSoldier = MercPtrs[ usSoldierIndex ]; - if ( fFromUI ) + // anv: don't try to heal interactive spots + if (fFromUI && Item[usHandItem].usItemClass != IC_MEDKIT) { INT32 sInteractiveGridNo; @@ -325,6 +326,13 @@ INT32 HandleItem( SOLDIERTYPE *pSoldier, INT32 sGridNo, INT8 bLevel, UINT16 usHa { if (pTargetSoldier->bTeam == gbPlayerNum || pTargetSoldier->aiData.bNeutral) { + // anv: don't try to attack yourself, it will only cause deadlock + if (pSoldier == pTargetSoldier) + { + TacticalCharacterDialogue(pSoldier, QUOTE_REFUSING_ORDER); + return(ITEM_HANDLE_REFUSAL); + } + // nice mercs won't shoot other nice guys or neutral civilians if ((gMercProfiles[pSoldier->ubProfile].ubMiscFlags3 & PROFILE_MISC_FLAG3_GOODGUY) && ((pTargetSoldier->ubProfile == NO_PROFILE && pTargetSoldier->aiData.bNeutral && pTargetSoldier->ubBodyType != CROW) || From 1289c399ce8bffcaac099dd50c169339dabddef4 Mon Sep 17 00:00:00 2001 From: kitty624 <58940527+kitty624@users.noreply.github.com> Date: Tue, 22 Aug 2023 00:22:20 +0200 Subject: [PATCH 06/24] Update XML.h - added path to sub-directory for Extra Items - to avoid cluttering up the parent directory when using this feature - it allows to place extra items in sector via xml and based on difficulty level - the new directory "ExtraItems" will contain more details on how-to-use --- Tactical/XML.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tactical/XML.h b/Tactical/XML.h index 3505f1bbe..03a958119 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -157,8 +157,8 @@ typedef PARSE_STAGE; #define ALTSECTORSFILENAME "Map\\AltSectors.xml" #define SAMSITESFILENAME "Map\\SamSites.xml" #define HELISITESFILENAME "Map\\HeliSites.xml" -#define EXTRAITEMSFILENAME "Map\\A9_0_ExtraItems" // ".xml" will be added @runtime -#define EXTRAITEMSFILENAME2 "Map\\A11_0_ExtraItems" // ".xml" will be added @runtime +#define EXTRAITEMSFILENAME "Map\\ExtraItems\\A9_0_ExtraItems" // ".xml" will be added @runtime +#define EXTRAITEMSFILENAME2 "Map\\ExtraItems\\A11_0_ExtraItems" // ".xml" will be added @runtime #define SHIPPINGDESTINATIONSFILENAME "Map\\ShippingDestinations.xml" #define DELIVERYMETHODSFILENAME "Map\\DeliveryMethods.xml" #define DELIVERYMETHODSFILENAME "Map\\DeliveryMethods.xml" From 6bae48658e66dc7bc732874f9c4cf163c38ba2ff Mon Sep 17 00:00:00 2001 From: rftrdev <102184004+rftrdev@users.noreply.github.com> Date: Wed, 23 Aug 2023 00:34:54 -0700 Subject: [PATCH 07/24] Fix personality check for backgrounds during IMP creation (#208) --- Laptop/IMP Background.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Laptop/IMP Background.cpp b/Laptop/IMP Background.cpp index 9ea5c40e7..b7cddab42 100644 --- a/Laptop/IMP Background.cpp +++ b/Laptop/IMP Background.cpp @@ -15,6 +15,7 @@ #include "CharProfile.h" #include "soldier profile type.h" #include "IMP Compile Character.h" + #include "IMP Disability Trait.h" #include "GameSettings.h" #include "Interface.h" @@ -671,7 +672,7 @@ BOOLEAN IsBackGroundAllowed( UINT16 ubNumber ) return FALSE; } - switch ( iPersonality ) + switch ( iChosenDisabilityTrait() ) { case HEAT_INTOLERANT: if ( zBackground[ ubNumber ].value[BG_DESERT] > 0 ) @@ -807,4 +808,4 @@ void BtnIMPBackgroundPreviousCallback(GUI_BUTTON *btn,INT32 reason) usBackground = 0; } } -} \ No newline at end of file +} From 8ec02b2d40b0c81eb1e3a6a58188b384b0c8dae4 Mon Sep 17 00:00:00 2001 From: rftrdev <102184004+rftrdev@users.noreply.github.com> Date: Wed, 23 Aug 2023 01:21:22 -0700 Subject: [PATCH 08/24] Add version info to main menu (#209) --- MainMenuScreen.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/MainMenuScreen.cpp b/MainMenuScreen.cpp index 1db5e1747..bebe6193c 100644 --- a/MainMenuScreen.cpp +++ b/MainMenuScreen.cpp @@ -855,6 +855,9 @@ void RenderMainMenu() DrawTextToScreen( L"BLOCKFONTNARROW: "/*gzCopyrightText[ 0 ]*/, 0, 445, 640, BLOCKFONTNARROW, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); DrawTextToScreen( L"FONT14HUMANIST: "/*gzCopyrightText[ 0 ]*/, 0, 465, 640, FONT14HUMANIST, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); #else + CHAR16 text[128]; + swprintf(text, L"%s: %s %S %s", pMessageStrings[ MSG_VERSION ], zProductLabel, czVersionString, zBuildInformation ); + DrawTextToScreen( text, 10, 10, SCREEN_WIDTH, TINYFONT1, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, LEFT_JUSTIFIED ); DrawTextToScreen( gzCopyrightText[ 0 ], 0, SCREEN_HEIGHT - 20, SCREEN_WIDTH, FONT10ARIAL, FONT_MCOLOR_WHITE, FONT_MCOLOR_BLACK, FALSE, CENTER_JUSTIFIED ); #endif From 3ddacf1a44fd5e378fbca8dac8845f2dfa4f1f25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 28 Aug 2023 15:26:02 +0200 Subject: [PATCH 09/24] Fix vehicles consuming fuel after instant movement cancel (#213) --- Strategic/Strategic Movement.cpp | 44 ++++++++++++++++---------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Strategic/Strategic Movement.cpp b/Strategic/Strategic Movement.cpp index 68eb7123f..cbabfed6e 100644 --- a/Strategic/Strategic Movement.cpp +++ b/Strategic/Strategic Movement.cpp @@ -1951,33 +1951,33 @@ void GroupArrivedAtSector( UINT8 ubGroupID, BOOLEAN fCheckForBattle, BOOLEAN fNe SectorInfo[SECTOR( pGroup->ubSectorX, pGroup->ubSectorY )].bLastKnownEnemies = NumNonPlayerTeamMembersInSector( pGroup->ubSectorX, pGroup->ubSectorY, ENEMY_TEAM ); } - // award life 'experience' for travelling, based on travel time! - if ( !pGroup->fVehicle ) + // Flugente: do not award experience gain if we never left + if (!fNeverLeft) { - // Flugente: do not award experience gain if we never left - if ( !fNeverLeft ) + // award life 'experience' for travelling, based on travel time! + if (!pGroup->fVehicle) { // gotta be walking to get tougher - AwardExperienceForTravelling( pGroup ); + AwardExperienceForTravelling(pGroup); } - } - else if( !IsGroupTheHelicopterGroup( pGroup ) ) - { - SOLDIERTYPE *pSoldier; - INT32 iVehicleID; - iVehicleID = GivenMvtGroupIdFindVehicleId( pGroup->ubGroupID ); - AssertMsg( iVehicleID != -1, "GroupArrival for vehicle group. Invalid iVehicleID. " ); - - pSoldier = GetSoldierStructureForVehicle( iVehicleID ); - AssertMsg( pSoldier, "GroupArrival for vehicle group. Invalid soldier pointer." ); - - SpendVehicleFuel( pSoldier, (INT16)(pGroup->uiTraverseTime*6) ); - - if( !VehicleFuelRemaining( pSoldier ) ) + else if (!IsGroupTheHelicopterGroup(pGroup)) { - ReportVehicleOutOfGas( iVehicleID, pGroup->ubSectorX, pGroup->ubSectorY ); - //Nuke the group's path, so they don't continue moving. - ClearMercPathsAndWaypointsForAllInGroup( pGroup ); + SOLDIERTYPE* pSoldier; + INT32 iVehicleID; + iVehicleID = GivenMvtGroupIdFindVehicleId(pGroup->ubGroupID); + AssertMsg(iVehicleID != -1, "GroupArrival for vehicle group. Invalid iVehicleID. "); + + pSoldier = GetSoldierStructureForVehicle(iVehicleID); + AssertMsg(pSoldier, "GroupArrival for vehicle group. Invalid soldier pointer."); + + SpendVehicleFuel(pSoldier, (INT16)(pGroup->uiTraverseTime * 6)); + + if (!VehicleFuelRemaining(pSoldier)) + { + ReportVehicleOutOfGas(iVehicleID, pGroup->ubSectorX, pGroup->ubSectorY); + //Nuke the group's path, so they don't continue moving. + ClearMercPathsAndWaypointsForAllInGroup(pGroup); + } } } } From d56288e313729b2c6eecde92eb65e04ea01bec1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 28 Aug 2023 15:26:34 +0200 Subject: [PATCH 10/24] Fixed incorrect vehicle group id after reloading the game and vehicle movement plot issues. (#207) --- Strategic/Map Screen Interface.cpp | 42 ++++++++++++++++++++++++------ Tactical/Squads.cpp | 15 ++++++++++- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/Strategic/Map Screen Interface.cpp b/Strategic/Map Screen Interface.cpp index 37eb3087b..678c4fd7a 100644 --- a/Strategic/Map Screen Interface.cpp +++ b/Strategic/Map Screen Interface.cpp @@ -4582,6 +4582,28 @@ void HandleSettingTheSelectedListOfMercs( void ) INT8 pbErrorNumber = -1; pSoldier = MercPtrs[gCharactersList[GetSelectedDestChar()].usSolID]; INT8 bSquadValue = pSoldier->bAssignment; + if (bSquadValue == VEHICLE) + { + for (INT8 bCounter = 0; bCounter < NUMBER_OF_SQUADS; ++bCounter) + { + if (Squad[bCounter][0] != NULL && IsVehicle(Squad[bCounter][0]) && + Squad[bCounter][0]->bVehicleID == pSoldier->iVehicleId) + { + bSquadValue = bCounter; + break; + } + } + } + if (bSquadValue >= NUMBER_OF_SQUADS) + { + if (pbErrorNumber != -1) + { + ReportMapScreenMovementError(pbErrorNumber); + } + SetSelectedDestChar(-1); + giDestHighLine = -1; + return; + } // find number of characters in particular squad. for (INT8 bCounter = 0; bCounter < NUMBER_OF_SOLDIERS_PER_SQUAD; ++bCounter) @@ -4693,17 +4715,21 @@ INT8 FindSquadThatSoldierCanJoin( SOLDIERTYPE *pSoldier ) // run through the list of squads for( bCounter = 0; bCounter < NUMBER_OF_SQUADS; bCounter++ ) { - // is this squad in this sector - if( IsThisSquadInThisSector( pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ, bCounter ) ) + // anv: don't automatically put people in vehicle squads + if (Squad[bCounter][0] == NULL || !IsVehicle(Squad[bCounter][0])) { - // does it have room? - if( IsThisSquadFull( bCounter ) == FALSE ) + // is this squad in this sector + if (IsThisSquadInThisSector(pSoldier->sSectorX, pSoldier->sSectorY, pSoldier->bSectorZ, bCounter)) { - // is it doing the same thing as the soldier is (staying or going) ? - if( IsSquadSelectedForMovement( bCounter ) == IsSoldierSelectedForMovement( pSoldier ) ) + // does it have room? + if (IsThisSquadFull(bCounter) == FALSE) { - // go ourselves a match, then - return( bCounter ); + // is it doing the same thing as the soldier is (staying or going) ? + if (IsSquadSelectedForMovement(bCounter) == IsSoldierSelectedForMovement(pSoldier)) + { + // go ourselves a match, then + return(bCounter); + } } } } diff --git a/Tactical/Squads.cpp b/Tactical/Squads.cpp index 7fb7c6b30..a7dda1b15 100644 --- a/Tactical/Squads.cpp +++ b/Tactical/Squads.cpp @@ -1700,7 +1700,20 @@ void CheckSquadMovementGroups( void ) for (INT8 iSoldier = 0; iSoldier < NUMBER_OF_SOLDIERS_PER_SQUAD; iSoldier++) { if (Squad[iSquad][iSoldier] != NULL) { - Squad[iSquad][iSoldier]->ubGroupID = pGroup->ubGroupID; + if (IsVehicle(Squad[iSquad][iSoldier])) + { + INT32 iCounter = 0; + for (iCounter = 0; iCounter < ubNumberOfVehicles; iCounter++) + { + if (pVehicleList[iCounter].ubProfileID == Squad[iSquad][iSoldier]->ubProfile) + break; + } + Squad[iSquad][iSoldier]->ubGroupID = pVehicleList[iCounter].ubMovementGroup; + } + else + { + Squad[iSquad][iSoldier]->ubGroupID = pGroup->ubGroupID; + } } } } From 7edecd68495723af86bf62fd6b1c18a592d96615 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 28 Aug 2023 15:26:46 +0200 Subject: [PATCH 11/24] Fixed vehicle body not properly removed when destroyed during ramming (#210) --- Tactical/Soldier Control.cpp | 20 ++++++++++++++++++++ Tactical/Soldier Control.h | 8 ++++++++ TileEngine/structure.cpp | 4 ++-- 3 files changed, 30 insertions(+), 2 deletions(-) diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 9a594ee2f..271368d04 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -10700,6 +10700,22 @@ UINT8 SOLDIERTYPE::SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sBr return(ubCombinedLoss); } +void SOLDIERTYPE::SoldierTakeDelayedDamage(INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathLoss, UINT8 ubReason, UINT8 ubAttacker, INT32 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage) +{ + delayedDamageFunction = [this, bHeight, sLifeDeduct, sBreathLoss, ubReason, ubAttacker, sSourceGrid, sSubsequent, fShowDamage]() + { + this->SoldierTakeDamage(bHeight, sLifeDeduct, sBreathLoss, ubReason, ubAttacker, sSourceGrid, sSubsequent, fShowDamage); + }; +} + +void SOLDIERTYPE::ResolveDelayedDamage() +{ + if (delayedDamageFunction) + { + delayedDamageFunction(); + delayedDamageFunction = nullptr; + } +} extern BOOLEAN IsMercSayingDialogue( UINT8 ubProfileID ); @@ -11499,6 +11515,8 @@ void SOLDIERTYPE::MoveMerc( FLOAT dMovementChange, FLOAT dAngle, BOOLEAN fCheckR // OK, set new position this->EVENT_InternalSetSoldierPosition( dXPos, dYPos, FALSE, FALSE, FALSE ); + this->ResolveDelayedDamage(); + // Flugente: drag people if ( currentlydragging ) { @@ -26234,4 +26252,6 @@ void SOLDIERTYPE::InitializeExtraData(void) this->ubQuickItemSlot = 0; this->usGrenadeItem = 0; + + this->delayedDamageFunction = nullptr; } diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index e02e1de0a..697456188 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -20,6 +20,7 @@ #include #include "GameSettings.h" // added by Flugente #include "Disease.h" // added by Flugente +#include #define PTR_CIVILIAN (pSoldier->bTeam == CIV_TEAM) #define PTR_CROUCHED (gAnimControl[ pSoldier->usAnimState ].ubHeight == ANIM_CROUCH) @@ -1656,6 +1657,10 @@ public: UINT8 ubQuickItemSlot; UINT16 usGrenadeItem; + + // anv: resolve damage with delay, e.g. damage applied mid movement that would cause issues with world data if applied immediately + std::function delayedDamageFunction; + public: // CREATION FUNCTIONS BOOLEAN DeleteSoldier( void ); @@ -1719,6 +1724,9 @@ public: void ReviveSoldier( void ); UINT8 SoldierTakeDamage( INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathDeduct, UINT8 ubReason, UINT8 ubAttacker, INT32 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage ); + // anv: resolve damage with delay, e.g. damage applied mid movement that would cause issues with world data if applied immediately + void SoldierTakeDelayedDamage(INT8 bHeight, INT16 sLifeDeduct, INT16 sBreathDeduct, UINT8 ubReason, UINT8 ubAttacker, INT32 sSourceGrid, INT16 sSubsequent, BOOLEAN fShowDamage); + void ResolveDelayedDamage(); // Palette functions for soldiers BOOLEAN CreateSoldierPalettes( void ); diff --git a/TileEngine/structure.cpp b/TileEngine/structure.cpp index 49264a512..1d8b2fc22 100644 --- a/TileEngine/structure.cpp +++ b/TileEngine/structure.cpp @@ -1914,10 +1914,10 @@ INT8 DamageStructure( STRUCTURE * pStructure, UINT8 ubDamage, UINT8 ubReason, IN //Since the structure is being damaged, set the map element that a structure is damaged gpWorldLevelData[ tmpgridno ].uiFlags |= MAPELEMENT_STRUCTURE_DAMAGED; - // handle structure revenge - damage to vehicle + // handle structure revenge - damage to vehicle - to be resolved after movement if ( ubOwner != NOBODY && MercPtrs[ubOwner] && !ARMED_VEHICLE( MercPtrs[ubOwner] ) ) { - MercPtrs[ ubOwner ]->SoldierTakeDamage( 0, Random(max(0,(ubBaseArmour-10)/5))+max(0,(ubBaseArmour-10)/5), 0, TAKE_DAMAGE_STRUCTURE_EXPLOSION, NOBODY, MercPtrs[ ubOwner ]->sGridNo, 0, TRUE ); + MercPtrs[ubOwner]->SoldierTakeDelayedDamage(0, Random(max(0,(ubBaseArmour-10)/5)) + max(0,(ubBaseArmour-10)/5), 0, TAKE_DAMAGE_STRUCTURE_EXPLOSION, NOBODY, MercPtrs[ ubOwner ]->sGridNo, 0, TRUE); } // recompile = TRUE means that we destroyed something From ee3d2472e720739add6445ad9bf01e2dc613b8a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Tue, 29 Aug 2023 21:25:14 +0200 Subject: [PATCH 12/24] Removed unnecessary offset for face gear --- Tactical/Faces.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tactical/Faces.cpp b/Tactical/Faces.cpp index aad597420..059ba8271 100644 --- a/Tactical/Faces.cpp +++ b/Tactical/Faces.cpp @@ -1467,8 +1467,8 @@ void GetXYForRightIconPlacement_FaceGera( FACETYPE *pFace, UINT16 ubIndex, INT16 usHeight = pTrav->usHeight; usWidth = pTrav->usWidth; - sX = sFaceX + ( usWidth * bNumIcons ) + 1; - sY = sFaceY + pFace->usFaceHeight - usHeight - 1; + sX = sFaceX + ( usWidth * bNumIcons ); + sY = sFaceY + pFace->usFaceHeight - usHeight; *psX = sX; *psY = sY; From 7910be0ab7119895b1ab2fc166ad909e8d59f7b6 Mon Sep 17 00:00:00 2001 From: kitty624 <58940527+kitty624@users.noreply.github.com> Date: Wed, 30 Aug 2023 19:34:30 +0200 Subject: [PATCH 13/24] Fix personality check for backgrounds during IMP creation --- Laptop/IMP Background.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Laptop/IMP Background.cpp b/Laptop/IMP Background.cpp index b7cddab42..ec2694235 100644 --- a/Laptop/IMP Background.cpp +++ b/Laptop/IMP Background.cpp @@ -16,6 +16,7 @@ #include "soldier profile type.h" #include "IMP Compile Character.h" #include "IMP Disability Trait.h" + #include "IMP Character Trait.h" #include "GameSettings.h" #include "Interface.h" @@ -708,7 +709,7 @@ BOOLEAN IsBackGroundAllowed( UINT16 ubNumber ) break; } - switch ( iAttitude ) + switch ( iChosenCharacterTrait() ) { case CHAR_TRAIT_SOCIABLE: if ( zBackground[ ubNumber ].uiFlags & BACKGROUND_XENOPHOBIC ) From 28aa73a859d71f802d961dfed617a7ea8b01cf8a Mon Sep 17 00:00:00 2001 From: rftrdev <102184004+rftrdev@users.noreply.github.com> Date: Sat, 2 Sep 2023 23:01:17 -0700 Subject: [PATCH 14/24] Clamp coolness to valid values when generating ARC ammo cache (#220) Fixes #217 --- Strategic/Rebel Command.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Strategic/Rebel Command.cpp b/Strategic/Rebel Command.cpp index b5ce30aa0..84f6f69f1 100644 --- a/Strategic/Rebel Command.cpp +++ b/Strategic/Rebel Command.cpp @@ -4429,7 +4429,8 @@ void SetupInfo() && (gGameOptions.ubGameStyle == STYLE_SCIFI || !Item[i].scifi)) { // coolness runs from 1-10, so apply offset - ItemIdCache::ammo[Item[i].ubCoolness-1].push_back(i); + const UINT8 coolness = min(max(1, Item[i].ubCoolness), 10); + ItemIdCache::ammo[coolness-1].push_back(i); } } } From 23ceb0fa4c7bfa3946f85fb881eee7709ad2c568 Mon Sep 17 00:00:00 2001 From: Asdow <20314541+Asdow@users.noreply.github.com> Date: Tue, 5 Sep 2023 17:33:42 +0300 Subject: [PATCH 15/24] Fix bug in OCTH prone CTH calculation (by Seven) (#221) GetToHitBonus() expects BOOLEAN for fProneStance argument, with no comparison to ANIM_PRONE, the ubEndHeight ends up adding bipod bonuses to *all* stances --- Tactical/Weapons.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 55acbf88a..5e9350a6c 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -7480,7 +7480,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, else { INT16 moda = GetToHitBonus(pInHand, iRange, bLightLevel, stance && iRange > MIN_PRONE_RANGE); - INT16 modb = GetToHitBonus(pInHand, iRange, bLightLevel, gAnimControl[pSoldier->usAnimState].ubEndHeight && iRange > MIN_PRONE_RANGE); + INT16 modb = GetToHitBonus(pInHand, iRange, bLightLevel, gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE && iRange > MIN_PRONE_RANGE); iChance += (INT32)((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb) / 100); } From fc5573f73d7425070f8d6fc33fee6ab4c7f1894e Mon Sep 17 00:00:00 2001 From: Asdow <20314541+Asdow@users.noreply.github.com> Date: Tue, 5 Sep 2023 18:57:40 +0300 Subject: [PATCH 16/24] Correct check for stance (#222) Same issue as in commit 23ceb0f --- Tactical/Weapons.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tactical/Weapons.cpp b/Tactical/Weapons.cpp index 5e9350a6c..667f9be46 100644 --- a/Tactical/Weapons.cpp +++ b/Tactical/Weapons.cpp @@ -7479,7 +7479,7 @@ UINT32 CalcChanceToHitGun(SOLDIERTYPE *pSoldier, INT32 sGridNo, INT16 ubAimTime, } else { - INT16 moda = GetToHitBonus(pInHand, iRange, bLightLevel, stance && iRange > MIN_PRONE_RANGE); + INT16 moda = GetToHitBonus(pInHand, iRange, bLightLevel, stance == ANIM_PRONE && iRange > MIN_PRONE_RANGE); INT16 modb = GetToHitBonus(pInHand, iRange, bLightLevel, gAnimControl[pSoldier->usAnimState].ubEndHeight == ANIM_PRONE && iRange > MIN_PRONE_RANGE); iChance += (INT32)((gGameExternalOptions.ubProneModifierPercentage * moda + (100 - gGameExternalOptions.ubProneModifierPercentage) * modb) / 100); } From 4c861e441af4dc4fca87f04f428f98ab192d2c5b Mon Sep 17 00:00:00 2001 From: sun-alf Date: Fri, 8 Sep 2023 15:55:01 +0300 Subject: [PATCH 17/24] [Fix] Revive FPS drawing overlay To enable built-in FPS, change gbFPSDisplay to SHOW_FULL_FPS (1) in debugger (or change it in src code and rebuild). --- TileEngine/Render Dirty.cpp | 6 ++++++ TileEngine/Render Dirty.h | 3 ++- gamescreen.cpp | 5 +++-- jascreens.cpp | 43 +++++++++++++------------------------ 4 files changed, 26 insertions(+), 31 deletions(-) diff --git a/TileEngine/Render Dirty.cpp b/TileEngine/Render Dirty.cpp index f90b3d255..1e22feb64 100644 --- a/TileEngine/Render Dirty.cpp +++ b/TileEngine/Render Dirty.cpp @@ -1009,6 +1009,12 @@ BOOLEAN UpdateVideoOverlay( VIDEO_OVERLAY_DESC *pTopmostDesc, UINT32 iBlitterInd } } + if ( uiFlags & VOVERLAY_DESC_FONT ) + { + gVideoOverlays[iBlitterIndex].uiFontID = pTopmostDesc->uiFontID; + gVideoOverlays[iBlitterIndex].ubFontBack = pTopmostDesc->ubFontBack; + gVideoOverlays[iBlitterIndex].ubFontFore = pTopmostDesc->ubFontFore; + } if ( uiFlags & VOVERLAY_DESC_DISABLED ) { diff --git a/TileEngine/Render Dirty.h b/TileEngine/Render Dirty.h index e3171deba..113f0766f 100644 --- a/TileEngine/Render Dirty.h +++ b/TileEngine/Render Dirty.h @@ -15,9 +15,10 @@ #define VOVERLAY_STARTDISABLED 0x00000002 -#define VOVERLAY_DESC_TEXT 0x00001000 +#define VOVERLAY_DESC_TEXT 0x00001000 #define VOVERLAY_DESC_DISABLED 0x00002000 #define VOVERLAY_DESC_POSITION 0x00004000 +#define VOVERLAY_DESC_FONT 0x00008000 // STRUCTURES diff --git a/gamescreen.cpp b/gamescreen.cpp index 67e70d99f..28fd812d0 100644 --- a/gamescreen.cpp +++ b/gamescreen.cpp @@ -166,6 +166,7 @@ UINT32 MainGameScreenInit(void) UnLockVideoSurface( FRAME_BUFFER); InitializeBackgroundRects(); + InitializeBaseDirtyRectQueue(); //EnvSetTimeInHours(ENV_TIME_12); @@ -188,8 +189,8 @@ UINT32 MainGameScreenInit(void) giFPSOverlay = RegisterVideoOverlay( ( VOVERLAY_STARTDISABLED | VOVERLAY_DIRTYBYTEXT ), &VideoOverlayDesc ); // SECOND, PERIOD COUNTER - VideoOverlayDesc.sLeft = 30; - VideoOverlayDesc.sTop = 0; + VideoOverlayDesc.sLeft = 0; + VideoOverlayDesc.sTop = 12; VideoOverlayDesc.sX = VideoOverlayDesc.sLeft; VideoOverlayDesc.sY = VideoOverlayDesc.sTop; swprintf( VideoOverlayDesc.pzText, L"Levelnodes: 100000" ); diff --git a/jascreens.cpp b/jascreens.cpp index 80f5188a1..f921cb0bb 100644 --- a/jascreens.cpp +++ b/jascreens.cpp @@ -122,46 +122,33 @@ void DisplayFrameRate( ) uiFrameCount = 0; } - // Create string - SetFont( SMALLFONT1 ); - - //DebugMsg(TOPIC_JA2, DBG_LEVEL_0, String( "FPS: %d ", __min( uiFPS, 1000 ) ) ); - - if ( uiFPS < 20 ) - { - SetFontBackground( FONT_MCOLOR_BLACK ); - SetFontForeground( FONT_MCOLOR_LTRED ); - } - else - { - SetFontBackground( FONT_MCOLOR_BLACK ); - SetFontForeground( FONT_MCOLOR_DKGRAY ); - } - if ( gbFPSDisplay == SHOW_FULL_FPS ) { + memset(&VideoOverlayDesc, 0, sizeof(VideoOverlayDesc)); + // FRAME RATE - memset( &VideoOverlayDesc, 0, sizeof( VideoOverlayDesc ) ); - swprintf( VideoOverlayDesc.pzText, L"%ld", __min( uiFPS, 1000 ) ); - VideoOverlayDesc.uiFlags = VOVERLAY_DESC_TEXT; + VideoOverlayDesc.uiFontID = SMALLFONT1; + VideoOverlayDesc.ubFontBack = FONT_MCOLOR_BLACK; + VideoOverlayDesc.ubFontFore = uiFPS < 20 ? FONT_MCOLOR_LTRED : FONT_MCOLOR_DKGRAY; + swprintf( VideoOverlayDesc.pzText, L"FPS: %ld", __min( uiFPS, 1000 ) ); + VideoOverlayDesc.uiFlags = VOVERLAY_DESC_TEXT | VOVERLAY_DESC_FONT | VOVERLAY_DESC_DISABLED; UpdateVideoOverlay( &VideoOverlayDesc, giFPSOverlay, FALSE ); // TIMER COUNTER - swprintf( VideoOverlayDesc.pzText, L"%ld", __min( giTimerDiag, 1000 ) ); - VideoOverlayDesc.uiFlags = VOVERLAY_DESC_TEXT; + swprintf( VideoOverlayDesc.pzText, L"Frame: %04ld ms", __min( giTimerDiag, 10000 ) ); + VideoOverlayDesc.uiFlags = VOVERLAY_DESC_TEXT | VOVERLAY_DESC_DISABLED; UpdateVideoOverlay( &VideoOverlayDesc, giCounterPeriodOverlay, FALSE ); - - if( GetMouseMapPos( &usMapPos) ) - { + //if( GetMouseMapPos( &usMapPos) ) + //{ //gprintfdirty( 0, 315, L"(%d)",sMapPos); //mprintf( 0,315,L"(%d)",sMapPos); - } - else - { + //} + //else + //{ //gprintfdirty( 0, 315, L"(%d %d)",gusMouseXPos, gusMouseYPos - INTERFACE_START_Y ); //mprintf( 0,315,L"(%d %d)",gusMouseXPos, gusMouseYPos - INTERFACE_START_Y ); - } + //} } if ( ( gTacticalStatus.uiFlags & GODMODE ) ) From f8637e59725ff7af3445d5aa687fed48720c5ff9 Mon Sep 17 00:00:00 2001 From: sun-alf Date: Fri, 8 Sep 2023 21:53:38 +0300 Subject: [PATCH 18/24] [~] Performance optimizations around Attachment[] and Launchable[] arrays AIMNAS with its ~50K elements in Attachment[] suffered of performance drop when MOLLE stuff is in the visible inventory. To resolve it the following is done: * Introduce gMAXATTACHMENTS_READ with actual number of elements in Attachment[]; according refactoring. * Introduce gMAXLAUNCHABLES_READ with actual number of elements in Launchable[]; according refactoring. * Introduce std::multimap AttachmentBackmap for quick access to attachments using itemId as a key. * Sort Attachment[] by attachmentIndex right after loading from XML. According order in XML is not needed anymore. * Introduce FindAttachmentRange() for quick access to attachments using attachmentId as a key (binary search in Attachment[]). In a few words, GetHelpTextForItemInLaptop(), ValidAttachment(), SetAttachmentSlotsFlag() were heavily optimized. --- Laptop/BobbyRGuns.cpp | 155 +++++++++++++++-------------------- Tactical/Interface Items.cpp | 34 +++----- Tactical/Item Types.h | 24 +++++- Tactical/Items.cpp | 133 +++++++++++++++++++----------- Tactical/Items.h | 1 + Tactical/XML_Attachments.cpp | 41 ++++++--- Tactical/XML_Launchable.cpp | 6 +- 7 files changed, 224 insertions(+), 170 deletions(-) diff --git a/Laptop/BobbyRGuns.cpp b/Laptop/BobbyRGuns.cpp index 479a1ef7d..01136a576 100644 --- a/Laptop/BobbyRGuns.cpp +++ b/Laptop/BobbyRGuns.cpp @@ -362,6 +362,29 @@ void GetHelpTextForItemInLaptop( STR16 pzStr, UINT16 usItemNumber ); void HandleBobbyRGunsKeyBoardInput(); void HandleBobbyRayMouseWheel(void); +// Appends source STR16 to target STR16 using decorators ("\n" and "..."). +// Returns TRUE if everything fits target, FALSE otherwise. +static BOOLEAN DecorateAppendString(STR16 target, size_t targetCapacity, STR16 source, UINT32 frontDecoratorsCnt = 1) +{ + const CHAR16 DECORATOR0[] = L"\n"; + const CHAR16 DECORATOR1[] = L"\n..."; + BOOLEAN result = FALSE; + size_t decoratorLen = wcslen(DECORATOR0) * frontDecoratorsCnt; + + if (wcslen(target) + decoratorLen + wcslen(source) + 1 < targetCapacity) + { + for (UINT32 i = 0; i < frontDecoratorsCnt; i++) + wcscat(target, DECORATOR0); + wcscat(target, source); + result = TRUE; + } + else if (wcslen(target) + wcslen(DECORATOR1) + 1 < targetCapacity) + { + wcscat(target, DECORATOR1); + } // otherwise don't even touch the target + return result; +} + void GameInitBobbyRGuns() { guiTempCurrentMode=0; @@ -4088,7 +4111,8 @@ void HandleBobbyRayMouseWheel(void) } void GetHelpTextForItemInLaptop( STR16 pzStr, UINT16 usItemNumber ) -{ +{ + const size_t ATTACHMENTS_STRBUF_SIZE = 3800; CHAR16 zItemName[ SIZE_ITEM_NAME ]; UINT8 ubItemCount=0; @@ -4115,12 +4139,9 @@ void GetHelpTextForItemInLaptop( STR16 pzStr, UINT16 usItemNumber ) // HEADROCK HAM 3: Variables for "Possible Attachment List" BOOLEAN fAttachmentsFound = FALSE; // Contains entire string of attachment names - CHAR16 attachStr[3900]; - // Contains current attachment string - CHAR16 attachStr2[100]; + CHAR16 attachStr[ATTACHMENTS_STRBUF_SIZE]; // Contains temporary attachment list before added to string constant from text.h - CHAR16 attachStr3[3900]; - UINT16 usAttachment; + CHAR16 attachStr3[ATTACHMENTS_STRBUF_SIZE]; CreateItem(usItemNumber, 100, &pObject); INT16 ubAttackAPs = BaseAPsToShootOrStab( APBPConstants[DEFAULT_APS], APBPConstants[DEFAULT_AIMSKILL], &pObject, NULL ); @@ -4146,109 +4167,69 @@ void GetHelpTextForItemInLaptop( STR16 pzStr, UINT16 usItemNumber ) else wcscat( apStr, L" / -" ); - // HEADROCK HAM 3: Empty these strings first, to avoid crashes. Please keep this here. - swprintf( attachStr, L"" ); - swprintf( attachStr2, L"" ); - swprintf( attachStr3, L"" ); + attachStr[0] = 0; + attachStr3[0] = 0; // HEADROCK HAM 3: Generate list of possible attachments to a gun (Guns only!) if (gGameExternalOptions.fBobbyRayTooltipsShowAttachments) { - UINT16 iLoop = 0; - // Check entire attachment list - while( 1 ) + if (UsingNewAttachmentSystem()) { - //Madd: Common Attachment Framework - //TODO: Note that the items in this list will be duplicated if they are present in both the CAF and the old attachment method - //need to refactor this to work more like the NAS attachment slots method - usAttachment = 0; - if ( IsAttachmentPointAvailable(Item[usItemNumber].uiIndex, iLoop) ) - { - usAttachment = iLoop; - // If the attachment is not hidden - if (usAttachment > 0 && !Item[ usAttachment ].hiddenaddon && !Item[ usAttachment ].hiddenattachment) - { - if (wcslen( attachStr3 ) + wcslen(Item[usAttachment].szItemName) > 3800) - { - // End list early to avoid stack overflow - wcscat( attachStr3, L"\n..." ); - break; - } - else - {// Add the attachment's name to the list. - fAttachmentsFound = TRUE; - swprintf( attachStr2, L"\n%s", Item[ usAttachment ].szItemName ); - wcscat( attachStr3, attachStr2); - } - } - } - - // Is the weapon we're checking the same as the one we're tooltipping? - usAttachment = 0; - if (Attachment[iLoop][1] == Item[usItemNumber].uiIndex) + std::pair::iterator, std::multimap::iterator> range; + std::multimap::iterator it; + range = AttachmentBackmap.equal_range(Item[usItemNumber].uiIndex); + for (it = range.first; it != range.second; it++) { - usAttachment = Attachment[iLoop][0]; - } - - // If the attachment is not hidden - if (usAttachment > 0 && !Item[ usAttachment ].hiddenaddon && !Item[ usAttachment ].hiddenattachment) - { - if (wcslen( attachStr3 ) + wcslen(Item[usAttachment].szItemName) > 3800) + UINT16 attachmentId = it->second.attachmentIndex; + if (!Item[attachmentId].hiddenaddon && !Item[attachmentId].hiddenattachment && ItemIsLegal(attachmentId, TRUE)) { - // End list early to avoid stack overflow - wcscat( attachStr3, L"\n..." ); - break; - } - else - {// Add the attachment's name to the list. fAttachmentsFound = TRUE; - swprintf( attachStr2, L"\n%s", Item[ usAttachment ].szItemName ); - wcscat( attachStr3, attachStr2); + if (DecorateAppendString(attachStr3, ATTACHMENTS_STRBUF_SIZE, Item[attachmentId].szItemName) == FALSE) + break; } } - - - iLoop++; - if (Attachment[iLoop][0] == 0 && Item[iLoop].usItemClass == 0) - { - // Reached end of list - break; - } } + else // old attachment system + { + for (UINT32 itemId = 1; itemId < gMAXITEMS_READ; itemId++) + { + // If the attachment is not hidden and attachable to the gun (usItemNumber) + if (!Item[itemId].hiddenaddon && !Item[itemId].hiddenattachment && + ItemIsLegal(itemId, TRUE) && IsAttachmentPointAvailable(Item[usItemNumber].uiIndex, itemId)) + { + fAttachmentsFound = TRUE; + if (DecorateAppendString(attachStr3, ATTACHMENTS_STRBUF_SIZE, Item[itemId].szItemName) == FALSE) + break; + } + } + } + if (fAttachmentsFound) { - // Add extra empty line and attachment list title - swprintf( attachStr, L"\n \n%s", gWeaponStatsDesc[ 14 ] ); - wcscat( attachStr, attachStr3 ); + DecorateAppendString(attachStr, ATTACHMENTS_STRBUF_SIZE, gWeaponStatsDesc[14], 2); // 2 new lines and "Attachments:" title + DecorateAppendString(attachStr, ATTACHMENTS_STRBUF_SIZE, attachStr3, 0); // no new line, list of attachments (starts with new line) } } //Sum up default attachments. BOOLEAN fFoundDefault = FALSE; - swprintf( attachStr2, L"" ); - swprintf( attachStr3, L"" ); - for(UINT8 cnt = 0; cnt < MAX_DEFAULT_ATTACHMENTS; cnt++){ - if(Item[usItemNumber].defaultattachments[cnt] != 0){ - if (wcslen( attachStr ) + wcslen(attachStr3) + wcslen(Item[ Item[usItemNumber].defaultattachments[cnt] ].szItemName) > 3800) - { - // End list early to avoid stack overflow - wcscat( attachStr3, L"\n..." ); + attachStr3[0] = 0; + for (UINT8 cnt = 0; cnt < MAX_DEFAULT_ATTACHMENTS; cnt++) + { + if (Item[usItemNumber].defaultattachments[cnt] != 0) + { + if (DecorateAppendString(attachStr3, ATTACHMENTS_STRBUF_SIZE, Item[Item[usItemNumber].defaultattachments[cnt]].szItemName) == FALSE) break; - } fFoundDefault = TRUE; - swprintf( attachStr2, L"\n%s", Item[ Item[usItemNumber].defaultattachments[cnt] ].szItemName ); - wcscat( attachStr3, attachStr2 ); - } else { - //If we found an empty entry, we can assume the rest will be empty too. - break; } + else // If we found an empty entry, we can assume the rest will be empty too. + break; } - if(fFoundDefault){ - //Found at least one default attachment, write it to the attachment string. - CHAR16 defaultStr[50]; - swprintf( defaultStr, L"\n \n%s", gWeaponStatsDesc[ 17 ] ); - wcscat( attachStr, defaultStr ); - wcscat( attachStr, attachStr3 ); + + if (fFoundDefault) + { + DecorateAppendString(attachStr, ATTACHMENTS_STRBUF_SIZE, gWeaponStatsDesc[17], 2); // 2 new lines and "Default:" title + DecorateAppendString(attachStr, ATTACHMENTS_STRBUF_SIZE, attachStr3, 0); // no new line, list of attachments (starts with new line) } // HEADROCK HAM 3: Added last string (attachStr), for display of the possible attachment list. diff --git a/Tactical/Interface Items.cpp b/Tactical/Interface Items.cpp index d71aa3ca4..9b7832044 100644 --- a/Tactical/Interface Items.cpp +++ b/Tactical/Interface Items.cpp @@ -2913,20 +2913,16 @@ void HandleAnyMercInSquadHasCompatibleStuff( UINT8 ubSquad, OBJECTTYPE *pObject, BOOLEAN IsMutuallyValidAttachmentOrLaunchable(UINT16 usAttItem, UINT16 usItem)//dnl ch76 091113 { - UINT32 uiLoop = 0; - while ( Attachment[uiLoop][0] ) + for (UINT32 uiLoop = 0; uiLoop < gMAXATTACHMENTS_READ; uiLoop++) { - if(Attachment[uiLoop][0] == usAttItem && Attachment[uiLoop][1] == usItem || Attachment[uiLoop][0] == usItem && Attachment[uiLoop][1] == usAttItem ) + if (Attachment[uiLoop].attachmentIndex == usAttItem && Attachment[uiLoop].itemIndex == usItem || Attachment[uiLoop].attachmentIndex == usItem && Attachment[uiLoop].itemIndex == usAttItem) return(TRUE); - ++uiLoop; } - uiLoop = 0; - while ( Launchable[uiLoop][0] ) + for (UINT32 uiLoop = 0; uiLoop < gMAXLAUNCHABLES_READ; uiLoop++) { if ( Launchable[uiLoop][0] == usAttItem && Launchable[uiLoop][1] == usItem || Launchable[uiLoop][0] == usItem && Launchable[uiLoop][1] == usAttItem ) return(TRUE); - ++uiLoop; } return(FALSE); @@ -5765,12 +5761,8 @@ void UpdateAttachmentTooltips(OBJECTTYPE *pObject, UINT8 ubStatusIndex) } // sevenfm: check launchables - for (UINT16 usLoop = 0; usLoop < MAXITEMS + 1; usLoop++) + for (UINT16 usLoop = 0; usLoop < gMAXLAUNCHABLES_READ; usLoop++) { - // check that reached end of valid launchables - if (Launchable[usLoop][0] == 0) - break; - usAttachment = 0; if (Launchable[usLoop][1] == pObject->usItem && AttachmentSlots[usLoopSlotID].nasAttachmentClass & Item[Launchable[usLoop][0]].nasAttachmentClass) { @@ -5801,17 +5793,14 @@ void UpdateAttachmentTooltips(OBJECTTYPE *pObject, UINT8 ubStatusIndex) } // check all attachments - for (UINT16 usLoop = 0; usLoop < MAXATTACHMENTS; usLoop++) + //TODO: should be optimized using AttachmentBackmap and/or possibly FindAttachmentRange() + for (UINT32 uiLoop = 0; uiLoop < gMAXATTACHMENTS_READ; uiLoop++) { - // check that reached end of valid attachments - if (Attachment[usLoop][0] == 0) - break; - usAttachment = 0; - if (Attachment[usLoop][1] == pObject->usItem && AttachmentSlots[usLoopSlotID].nasAttachmentClass & Item[Attachment[usLoop][0]].nasAttachmentClass) + if (Attachment[uiLoop].itemIndex == pObject->usItem && AttachmentSlots[usLoopSlotID].nasAttachmentClass & Item[Attachment[uiLoop].attachmentIndex].nasAttachmentClass) { //search primary item attachments.xml - usAttachment = Attachment[usLoop][0]; + usAttachment = Attachment[uiLoop].attachmentIndex; } else { @@ -5820,8 +5809,11 @@ void UpdateAttachmentTooltips(OBJECTTYPE *pObject, UINT8 ubStatusIndex) UINT16* p = cnt ? &attachedList.front() : NULL; while (cnt) { - if (Attachment[usLoop][1] == *p && AttachmentSlots[usLoopSlotID].nasAttachmentClass & Item[Attachment[usLoop][0]].nasAttachmentClass) - usAttachment = Attachment[usLoop][0]; + if (Attachment[uiLoop].itemIndex == *p && AttachmentSlots[usLoopSlotID].nasAttachmentClass & Item[Attachment[uiLoop].attachmentIndex].nasAttachmentClass) + { + usAttachment = Attachment[uiLoop].attachmentIndex; + break; + } cnt--, p++; } diff --git a/Tactical/Item Types.h b/Tactical/Item Types.h index 67e761d42..cdd3d2a19 100644 --- a/Tactical/Item Types.h +++ b/Tactical/Item Types.h @@ -1724,9 +1724,30 @@ typedef enum MAXITEMS = 16001 } ITEMDEFINE; +struct AttachmentStruct +{ + UINT16 attachmentIndex; + UINT16 itemIndex; + UINT16 APCost; + UINT16 NASOnly; + + bool operator<(const AttachmentStruct& a) const + { + bool result = false; + if (attachmentIndex < a.attachmentIndex) + result = true; + else if (attachmentIndex == a.attachmentIndex) + result = itemIndex < a.itemIndex; + + return result; + } +}; + // Flugente: in order not to loop over MAXITEMS items if we only have a few thousand, remember the actual number of items in the xml extern UINT32 gMAXITEMS_READ; extern UINT32 gMAXAMMOTYPES_READ; +extern UINT32 gMAXATTACHMENTS_READ; +extern UINT32 gMAXLAUNCHABLES_READ; /* CHRISL: Arrays to track ic group information. These allow us to determine which LBE slots control which pockets and what LBE class the pockets are.*/ @@ -1781,7 +1802,8 @@ const INT16 icDefault[NUM_INV_SLOTS] = { #define MAXATTACHMENTS 60000 extern INVTYPE Item[MAXITEMS]; -extern UINT16 Attachment[MAXATTACHMENTS][4]; +extern AttachmentStruct Attachment[MAXATTACHMENTS]; +extern std::multimap AttachmentBackmap; //WarmSteel - Here we have some definitions for NAS typedef struct diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index ba9d4e007..dd56f7f78 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -566,7 +566,8 @@ AttachmentInfoStruct AttachmentInfo[MAXITEMS+1];// = AttachmentSlotStruct AttachmentSlots[MAXITEMS+1]; ItemReplacementStruct ItemReplacement[MAXATTACHMENTS]; -UINT16 Attachment[MAXATTACHMENTS][4];// = +AttachmentStruct Attachment[MAXATTACHMENTS];// = +std::multimap AttachmentBackmap; // key is itemId //{ // {SILENCER, GLOCK_17}, // {SILENCER, GLOCK_18}, @@ -2253,7 +2254,6 @@ INT32 GetAttachmentInfoIndex( UINT16 usItem ) //Determine if it is possible to add this attachment to the item. BOOLEAN ValidAttachment( UINT16 usAttachment, UINT16 usItem, UINT8 * pubAPCost ) { - INT32 iLoop = 0; if (pubAPCost) { *pubAPCost = (UINT8)APBPConstants[AP_RELOAD_GUN]; //default value } @@ -2269,40 +2269,26 @@ BOOLEAN ValidAttachment( UINT16 usAttachment, UINT16 usItem, UINT8 * pubAPCost ) *pubAPCost = Item[usAttachment].ubAttachToPointAPCost; return TRUE; } + // look for the section of the array pertaining to this attachment... - while( 1 ) - { - if (Attachment[iLoop][0] == usAttachment) - { - break; - } - ++iLoop; - if (Attachment[iLoop][0] == 0) - { - // the proposed item cannot be attached to anything! - return( FALSE ); - } - } + UINT32 startIndex = 0, endIndex = 0; + if (FindAttachmentRange(usAttachment, &startIndex, &endIndex) == FALSE) + return FALSE; + // now look through this section for the item in question - while( 1 ) + for (UINT32 iLoop = startIndex; iLoop <= endIndex; iLoop++) { - if (Attachment[iLoop][1] == usItem) + if (Attachment[iLoop].itemIndex == usItem) { - if ( UsingNewAttachmentSystem( ) || Attachment[iLoop][3] != 1 ) + if ( UsingNewAttachmentSystem( ) || Attachment[iLoop].NASOnly != 1 ) { if (pubAPCost) - *pubAPCost = (UINT8)Attachment[iLoop][2]; //Madd: get ap cost of attaching items :) - break; + *pubAPCost = (UINT8)Attachment[iLoop].APCost; //Madd: get ap cost of attaching items :) } - } - ++iLoop; - if (Attachment[iLoop][0] != usAttachment) - { - // the proposed item cannot be attached to the item in question - return( FALSE ); + return TRUE; } } - return( TRUE ); + return FALSE; } BOOLEAN ValidAttachment( UINT16 usAttachment, OBJECTTYPE * pObj, UINT8 * pubAPCost, UINT8 subObject, std::vector usAttachmentSlotIndexVector) @@ -5677,40 +5663,40 @@ BOOLEAN OBJECTTYPE::AttachObjectNAS( SOLDIERTYPE * pSoldier, OBJECTTYPE * pAttac UINT64 SetAttachmentSlotsFlag(OBJECTTYPE* pObj) { UINT64 uiSlotFlag = 0; - UINT32 uiLoop = 0; - UINT32 fItem; if (pObj->exists() == false) return 0; - UINT64 point = GetAvailableAttachmentPoint(pObj, 0); - - while (uiLoop < gMAXITEMS_READ && Item[uiLoop].usItemClass != 0 || - uiLoop < MAXATTACHMENTS && Attachment[uiLoop][0] != 0 || - uiLoop < MAXITEMS + 1 && Launchable[uiLoop][0] != 0) + if (UsingNewAttachmentSystem()) { - if (uiLoop > 0 && uiLoop < gMAXITEMS_READ && IsAttachmentPointAvailable(point, uiLoop, TRUE)) + std::pair::iterator, std::multimap::iterator> range; + std::multimap::iterator it; + range = AttachmentBackmap.equal_range(pObj->usItem); + for (it = range.first; it != range.second; it++) { - fItem = uiLoop; - if (fItem && ItemIsLegal(fItem, TRUE)) - uiSlotFlag |= Item[fItem].nasAttachmentClass; + UINT16 attachmentId = it->second.attachmentIndex; + if (ItemIsLegal(attachmentId, TRUE)) + uiSlotFlag |= Item[attachmentId].nasAttachmentClass; } - if (uiLoop < MAXATTACHMENTS && Attachment[uiLoop][1] == pObj->usItem) + for (UINT32 i = 0; i < gMAXLAUNCHABLES_READ; i++) { - fItem = Attachment[uiLoop][0]; - if (fItem && ItemIsLegal(fItem, TRUE)) - uiSlotFlag |= Item[fItem].nasAttachmentClass; + if (Launchable[i][1] == pObj->usItem) + { + UINT16 attachmentId = Launchable[i][0]; + if (ItemIsLegal(attachmentId, TRUE)) + uiSlotFlag |= Item[attachmentId].nasAttachmentClass; + } } - - if (uiLoop < MAXITEMS + 1 && Launchable[uiLoop][1] == pObj->usItem) + } + else + { + UINT64 point = GetAvailableAttachmentPoint(pObj, 0); + for (UINT32 itemId = 1; itemId < gMAXITEMS_READ; itemId++) { - fItem = Launchable[uiLoop][0]; - if (fItem && ItemIsLegal(fItem, TRUE)) - uiSlotFlag |= Item[fItem].nasAttachmentClass; + if (ItemIsLegal(itemId, TRUE) && IsAttachmentPointAvailable(point, itemId, TRUE)) + uiSlotFlag |= Item[itemId].nasAttachmentClass; } - - uiLoop++; } return uiSlotFlag; @@ -16028,3 +16014,52 @@ UINT16 GetLaunchableOfExplosionType(UINT16 launcher, UINT8 explosionType) } return NOTHING; } + +BOOLEAN FindAttachmentRange(UINT16 usAttachment, UINT32* pStartIndex, UINT32* pEndIndex) +{ + BOOLEAN result = FALSE; + + INT32 leftMargin = 0; + INT32 rightMargin = (INT32)gMAXATTACHMENTS_READ - 1; + INT32 middle = 0; + // use binary search to locate the group of elements for given attachment item Id (usAttachment) + while (leftMargin <= rightMargin) + { + middle = leftMargin + (rightMargin - leftMargin) / 2; + + if (Attachment[middle].attachmentIndex == usAttachment) + { + result = TRUE; + break; + } + else if (Attachment[middle].attachmentIndex < usAttachment) + leftMargin = middle + 1; + else + rightMargin = middle - 1; + } + + if (result) + { + // now middle is an index somewhere within the group, seek for beginning and ending of the group + if (pStartIndex) + { + *pStartIndex = (UINT32)middle; + for (INT32 i = middle - 1; i >= leftMargin; i--) + if (Attachment[i].attachmentIndex == usAttachment) + *pStartIndex = (UINT32)i; + else + break; + } + if (pEndIndex) + { + *pEndIndex = (UINT32)middle; + for (INT32 i = middle + 1; i <= rightMargin; i++) + if (Attachment[i].attachmentIndex == usAttachment) + *pEndIndex = (UINT32)i; + else + break; + } + } + + return result; +} diff --git a/Tactical/Items.h b/Tactical/Items.h index bc0c49c69..ce23180f5 100644 --- a/Tactical/Items.h +++ b/Tactical/Items.h @@ -568,6 +568,7 @@ INT32 GetPercentRangeBonus( OBJECTTYPE * pObj ); UINT8 GetInventorySleepModifier( SOLDIERTYPE *pSoldier ); void AttachDefaultAttachments(OBJECTTYPE *pObj, BOOLEAN fAllDefaultAttachments=TRUE);//dnl ch75 261013 +BOOLEAN FindAttachmentRange(UINT16 usAttachment, UINT32* pStartIndex, UINT32* pEndIndex); // Flugente: is this object useable by militia? BOOLEAN ObjectIsMilitiaRelevant( OBJECTTYPE *pObj ); diff --git a/Tactical/XML_Attachments.cpp b/Tactical/XML_Attachments.cpp index 7eaa89362..fdb28fc99 100644 --- a/Tactical/XML_Attachments.cpp +++ b/Tactical/XML_Attachments.cpp @@ -20,6 +20,8 @@ struct } typedef attachmentParseData; +UINT32 gMAXATTACHMENTS_READ = 0; + static void XMLCALL attachmentStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts) { @@ -93,9 +95,9 @@ attachmentEndElementHandle(void *userData, const XML_Char *name) if(pData->curIndex < pData->maxArraySize) { //DebugMsg(TOPIC_JA2, DBG_LEVEL_3,"AttachmentStartElementHandle: writing attachment to array"); - Attachment[pData->curIndex][0] = pData->curAttachment[0]; //write the attachment into the table - Attachment[pData->curIndex][1] = pData->curAttachment[1]; - Attachment[pData->curIndex][2] = pData->curAttachment[2]; + Attachment[pData->curIndex].attachmentIndex = pData->curAttachment[0]; //write the attachment into the table + Attachment[pData->curIndex].itemIndex = pData->curAttachment[1]; + Attachment[pData->curIndex].APCost = pData->curAttachment[2]; } } else if(strcmp(name, "attachmentIndex") == 0) @@ -127,7 +129,25 @@ attachmentEndElementHandle(void *userData, const XML_Char *name) } +static void MapAttachments() +{ + std::list stdList; + std::list::iterator it; + UINT32 i = 0; + for (i = 0; i < gMAXATTACHMENTS_READ; i++) + { + stdList.push_back(Attachment[i]); + AttachmentBackmap.insert(std::make_pair(Attachment[i].itemIndex, Attachment[i])); + } + + stdList.sort(); + + for (it = stdList.begin(), i = 0; it != stdList.end(); it++, i++) + { + Attachment[i] = *it; + } +} BOOLEAN ReadInAttachmentStats(STR fileName) { @@ -171,7 +191,6 @@ BOOLEAN ReadInAttachmentStats(STR fileName) XML_SetUserData(parser, &pData); - if(!XML_Parse(parser, lpcBuffer, uiFSize, TRUE)) { CHAR8 errorBuf[511]; @@ -183,13 +202,15 @@ BOOLEAN ReadInAttachmentStats(STR fileName) return FALSE; } + gMAXATTACHMENTS_READ = pData.curIndex + 1; + MapAttachments(); + MemFree(lpcBuffer); - - XML_ParserFree(parser); return( TRUE ); } + BOOLEAN WriteAttachmentStats() { HWFILE hFile; @@ -208,10 +229,10 @@ BOOLEAN WriteAttachmentStats() { FilePrintf(hFile,"\t\r\n"); - FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt][0]); - FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt][1]); - FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt][2]); - FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt][3]); + FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt].attachmentIndex); + FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt].itemIndex); + FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt].APCost); + FilePrintf(hFile,"\t\t%d\r\n", Attachment[cnt].NASOnly); FilePrintf(hFile,"\t\r\n"); } diff --git a/Tactical/XML_Launchable.cpp b/Tactical/XML_Launchable.cpp index bec05f339..26994d2d4 100644 --- a/Tactical/XML_Launchable.cpp +++ b/Tactical/XML_Launchable.cpp @@ -19,6 +19,8 @@ struct } typedef launchableParseData; +UINT32 gMAXLAUNCHABLES_READ = 0; + static void XMLCALL launchableStartElementHandle(void *userData, const XML_Char *name, const XML_Char **atts) { @@ -168,9 +170,9 @@ BOOLEAN ReadInLaunchableStats(STR fileName) return FALSE; } + gMAXLAUNCHABLES_READ = pData.curIndex + 1; + MemFree(lpcBuffer); - - XML_ParserFree(parser); return( TRUE ); From 556641377c5651e5ddc51ee279d339564d5c2bdd Mon Sep 17 00:00:00 2001 From: zwwooooo Date: Sun, 10 Sep 2023 22:31:47 +0800 Subject: [PATCH 19/24] Chinese translation update(by TBS) --- Utils/_ChineseText.cpp | 52 +++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/Utils/_ChineseText.cpp b/Utils/_ChineseText.cpp index e587df2de..30fd7ce41 100644 --- a/Utils/_ChineseText.cpp +++ b/Utils/_ChineseText.cpp @@ -3568,8 +3568,8 @@ STR16 gpStrategicString[] = L"僵尸", //L"Zombie", L"土匪", //L"Bandit", L"土匪杀死了%d名平民,在%s分区。", //注:这里的%d和%s不可以随意放前面或后面,一定要按英文顺序,不然会出错。(%d和%s 在中文中不能反过来。) L"Bandits attack and kill %d civilians in sector %s.", - L"Transport group", - L"Transport group en route", + L"运输队", //L"Transport group", + L"运输队已出发", //L"Transport group en route", }; STR16 gpGameClockString[] = @@ -6264,7 +6264,7 @@ STR16 z113FeaturesToggleText[] = L"天气功能:暴风雪", //L"Weather: Snow", L"随机事件功能", //L"Mini Events", L"反抗军司令部功能", //L"Arulco Rebel Command", - L"Strategic Transport Groups", + L"战略运输队", //L"Strategic Transport Groups", }; STR16 z113FeaturesHelpText[] = @@ -6312,7 +6312,7 @@ STR16 z113FeaturesHelpText[] = L"|天|气|功|能|:|暴|风|雪\n \n覆盖 [Tactical Weather Settings] ALLOW_SNOW\n \n暴风雪降低了能见度。\n \n配置选项:\nSNOW_EVENTS_PER_DAY\nSNOW_CHANCE_PER_DAY\nSNOW_MIN_LENGTH_IN_MINUTES\nSNOW_MAX_LENGTH_IN_MINUTES\nWEAPON_RELIABILITY_REDUCTION_SNOW\nBREATH_GAIN_REDUCTION_SNOW\nVISUAL_DISTANCE_DECREASE_SNOW\nHEARING_REDUCTION_SNOW\n \n", //L"|W|e|a|t|h|e|r|: |S|n|o|w\nOverrides [Tactical Weather Settings] ALLOW_SNOW\n \nSnowstorms decrease visibility.\n \nConfigurable Options:\nSNOW_EVENTS_PER_DAY\nSNOW_CHANCE_PER_DAY\nSNOW_MIN_LENGTH_IN_MINUTES\nSNOW_MAX_LENGTH_IN_MINUTES\nWEAPON_RELIABILITY_REDUCTION_SNOW\nBREATH_GAIN_REDUCTION_SNOW\nVISUAL_DISTANCE_DECREASE_SNOW\nHEARING_REDUCTION_SNOW", L"|随|机|事|件|功|能\n \n覆盖 [Mini Events Settings] MINI_EVENTS_ENABLED\n \n可能发生一些随机互动事件。\n \n配置选项:\nMINI_EVENTS_MIN_HOURS_BETWEEN_EVENTS\nMINI_EVENTS_MAX_HOURS_BETWEEN_EVENTS\n \n详细信息请查看MiniEvents.lua。\n \n", //L"|M|i|n|i |E|v|e|n|t|s\nOverrides [Mini Events Settings] MINI_EVENTS_ENABLED\n \nRandom events can occur.\n \nConfigurable Options:\nMINI_EVENTS_MIN_HOURS_BETWEEN_EVENTS\nMINI_EVENTS_MAX_HOURS_BETWEEN_EVENTS\n \nSee MiniEvents.lua for more details.", L"|反|抗|军|司|令|部|功|能\n \n覆盖 [Rebel Command Settings] REBEL_COMMAND_ENABLED\n \n允许你升级占领的城镇,控制反抗军在战略层面上运作。\n \n详细的内容设定请查看RebelCommand_Settings.ini。\n \n", //L"|A|R|C\nOverrides [Rebel Command Settings] REBEL_COMMAND_ENABLED\n \nCommand the rebel movement at the strategic level, and upgrade captured towns.\n \nFor tweakable values, see RebelCommand_Settings.ini.", - L"|S|t|r|a|t|e|g|i|c |T|r|a|n|s|p|o|r|t |G|r|o|u|p|s\nOverrides [Strategic Gameplay Settings] STRATEGIC_TRANSPORT_GROUPS_ENABLED\n \nTransport groups carry valuable equipment across the map.\n \nConfigurable Options:\nMAX_SIMULTANEOUS_STRATEGIC_TRANSPORT_GROUPS", + L"|战|略|运|输|队\n \n覆盖 [Strategic Gameplay Settings] STRATEGIC_TRANSPORT_GROUPS_ENABLED\n \n运输队在地图上运送有价值的装备。\n \n配置选项: \nMAX_SIMULTANEOUS_STRATEGIC_TRANSPORT_GROUPS", //L"|S|t|r|a|t|e|g|i|c |T|r|a|n|s|p|o|r|t |G|r|o|u|p|s\nOverrides [Strategic Gameplay Settings] STRATEGIC_TRANSPORT_GROUPS_ENABLED\n \nTransport groups carry valuable equipment across the map.\n \nConfigurable Options:\nMAX_SIMULTANEOUS_STRATEGIC_TRANSPORT_GROUPS", }; STR16 z113FeaturesPanelText[] = @@ -6360,7 +6360,7 @@ STR16 z113FeaturesPanelText[] = L"启用暴风雪功能。在暴风雪中,更难被看到,武器退化更快,呼吸也更困难。", //L"Toggle snow. In a snowstorm, it is harder to see, weapons degrade faster, and it is a little harder to regain breath.", L"在游戏过程中,可能会弹出简短的事件。您可以从两个选项中选择一个,这可能会产生积极或消极的影响。事件可以影响各种各样的事情,但主要是你的佣兵。", //L"During the course of a campaign, brief events can pop up. You can select one of two responses, which may have positive and/or negative effects. Events can affect a wide variety of things, but mostly your mercs.", L"在完成反抗军食物运送任务后,你可以访问他们的(A.R.C)指挥部网站。在这里你可以设定反抗军的政策,也可以为占领区单独设置地方政策。这将带来丰厚的奖励。作为代价,城镇的民忠会上升得更慢,所以你需要更加努力地让当地人信任你。", //L"After completing the food delivery quest for the rebels, they will grant you access to their command website (A.R.C.). You can set the rebels' country-wide directive there, and capturing towns allows you to enact policies in that region that provide powerful bonuses. This comes at a price - town loyalty will rise slower, so you will need to work harder to have the locals trust you.", - L"The enemy sends groups across the map. If you can find and intercept them, they will probably have valuable gear. However, depending on your difficulty, each group that completes its transport mission provides the AI with strategic resources. Best experienced with Arulco Strategic Division enabled.", + L"敌人会在地图上派遣战略运输队,如果你能找到并截获它们就可能获取有价值的装备。但是,如果让敌人的运输队完成运输任务,那么就会给敌人提供战略资源(具体视难度而定)。要想获得最好体验,建议开启\"敌军战略司令部功能\"。", //L"The enemy sends groups across the map. If you can find and intercept them, they will probably have valuable gear. However, depending on your difficulty, each group that completes its transport mission provides the AI with strategic resources. Best experienced with Arulco Strategic Division enabled.", }; @@ -7672,7 +7672,7 @@ STR16 New113Message[] = L"无线电操作失败!", L"迫击炮弹不足,无法在分区发动密集轰炸!", L"Items.xml里没有定义信号弹物品!", - L"No High-Explosive shell item found in Items.xml!", + L"Items.xml里没有定义高爆弹物品!", //L"No High-Explosive shell item found in Items.xml!", L"未发现迫击炮,无法执行密集轰炸!", L"干扰信号成功,不需要重复操作!", L"正在监听周围声音,无需重复操作!", @@ -8693,7 +8693,7 @@ STR16 szUDBGenSecondaryStatsTooltipText[]= L"|医|用|夹|板", //L"|M|e|d|i|c|a|l |S|p|l|i|n|t", L"|阻|燃|弹|药", //L"|F|i|r|e |R|e|t|a|r|d|a|n|t |A|m|m|o", L"|燃|烧|弹|药", //L"|I|n|c|e|n|d|i|a|r|y |A|m|m|o", - L"|B|e|l|t| |F|e|d", + L"|弹|链|供|弹", //L"|B|e|l|t| |F|e|d", }; STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= @@ -8749,7 +8749,7 @@ STR16 szUDBGenSecondaryStatsExplanationsTooltipText[]= L"\n \n一旦应用, 这个物品可以提高对你的手臂\n或者腿部重伤的治疗速率。", //L"\n \nOnce applied, this item increases the healing\nspeed of severe wounds to either your arms or legs.", L"\n \n这种弹药可以灭火。", //L"\n \nThis ammo can extinguish fire.", L"\n \n这种弹药会引起燃烧(火灾)。", //L"\n \nThis ammo can cause fire.", - L"\n \nThis gun can be belt fed\nfrom a compatible LBE\nor by another merc.", + L"\n \n这种枪可以使用弹链供弹\n或者由LBE弹链供弹\n又或者由另一位佣兵供弹。", //L"\n \nThis gun can be belt fed\nfrom a compatible LBE\nor by another merc.", }; STR16 szUDBAdvStatsTooltipText[]= @@ -9330,7 +9330,7 @@ STR16 szBackgroundText_Value[]= L" 对某些其他背景的厌恶 \n", //L" dislikes some other backgrounds\n", L" 吸烟者", //L"Smoker", L" 非吸烟者", //L"Nonsmoker", - L" %s%d%% 蹲伏在可靠掩体后面对敌人的命中率 \n", //L" %s%d%% enemy CTH if crouched against thick cover in their direction\n", + L" %s%d%% 敌军对蹲伏在掩体后佣兵的命中率 \n", //L" %s%d%% enemy CTH if crouched against thick cover in their direction\n", L" %s%d%% 建设速度 \n",//L" %s%d%% building speed\n", L" 黑客技能:%s%d ",//L" hacking skill: %s%d ", L" %s%d%% 掩埋尸体速度 \n", //L" %s%d%% burial speed\n", @@ -11655,21 +11655,21 @@ STR16 szLaptopStatText[] = L"威胁对话", //L"Threaten approach", L"招募对话", //L"Recruit approach", - L"Stats will regress.", - L"Fast", - L"Average", - L"Slow", - L"Health growth", - L"Strength growth", - L"Agility growth", - L"Dexterity growth", - L"Wisdom growth", - L"Marksmanship growth", - L"Explosives growth", - L"Leadership growth", - L"Medical growth", - L"Mechanical growth", - L"Experience growth", + L"统计倒退数据。", //L"Stats will regress.", + L"快速", //L"Fast", + L"平均", //L"Average", + L"慢速", //L"Slow", + L"生命成长", //L"Health growth", + L"力量成长", //L"Strength growth", + L"敏捷成长", //L"Agility growth", + L"灵巧成长", //L"Dexterity growth", + L"智慧成长", //L"Wisdom growth", + L"枪法成长", //L"Marksmanship growth", + L"爆破成长", //L"Explosives growth", + L"领导成长", //L"Leadership growth", + L"医疗成长", //L"Medical growth", + L"机械成长", //L"Mechanical growth", + L"等级成长", //L"Experience growth", }; STR16 szGearTemplateText[] = @@ -12111,8 +12111,8 @@ STR16 szRebelCommandAgentMissionsText[] = L"协同行动,悄悄地抵进敌军,但是要小心:这可能会让你部署在劣势区域。当进攻敌军部队时,部署区会更大。", //L"Coordinate efforts to find ways to sneak up on the enemy, but be careful: it's equally possible to put yourself in a disadvantaged deployment area. When attacking enemy forces, the deployment area is much larger.", L"扰乱ASD", //L"Disrupt ASD", L"破坏Arulco特种部门(ASD)的日常行动。临时阻止ASD部署更多的机械化单位,并且大幅度降低他们的每日收入。", //L"Wreak havoc on the day-to-day operations of the Arulco Special Division. Temporarily prevent the ASD from deploying additional mechanised units, and drastically reduce their daily income.", - L"Forge Transport Orders", - L"Create a bogus supply request. An enemy transport group will be ordered to rendezvous at this agent's location.", + L"伪造运输订单", //L"Forge Transport Orders", + L"创建一个虚假的运输请求,敌方的运输队就会在这个地点位置集合。", //L"Create a bogus supply request. An enemy transport group will be ordered to rendezvous at this agent's location.", L"战略情报", //L"Strategic Intel", L"侦听敌人,发现敌军的攻击目标。当在战略地图上观察队伍时,敌军优先进攻的目标区域会被标红。", //L"Intercept plans and discover where enemies intend to strike. When viewing teams on the strategic map, sectors prioritised by the enemy will be marked in red.", L"强化本地商店", //L"Improve Local Shops", From 5bfd19e5a831b7f0eb118fe560616d9c13dd7ff8 Mon Sep 17 00:00:00 2001 From: sun-alf Date: Thu, 14 Sep 2023 01:03:20 +0300 Subject: [PATCH 20/24] [Fix] One of optimizations in (2482234e) commit is not suitable for MOLLE stuff in 1.13 It is revealed, MOLLE things in 1.13 are configured in XMLs using both NAS and OAS (old attachment system) methods even if NAS is enabled in New Game Options. Given this, logic of SetAttachmentSlotsFlag() cannot be splitted onto NAS and OAS parts, and both Attachment[] and AttachmentPoints must be checked disregarding the NAS option. --- Tactical/Items.cpp | 41 ++++++++++++++++++----------------------- 1 file changed, 18 insertions(+), 23 deletions(-) diff --git a/Tactical/Items.cpp b/Tactical/Items.cpp index dd56f7f78..8212c736d 100644 --- a/Tactical/Items.cpp +++ b/Tactical/Items.cpp @@ -5667,36 +5667,31 @@ UINT64 SetAttachmentSlotsFlag(OBJECTTYPE* pObj) if (pObj->exists() == false) return 0; - if (UsingNewAttachmentSystem()) + std::pair::iterator, std::multimap::iterator> range; + std::multimap::iterator it; + range = AttachmentBackmap.equal_range(pObj->usItem); + for (it = range.first; it != range.second; it++) { - std::pair::iterator, std::multimap::iterator> range; - std::multimap::iterator it; - range = AttachmentBackmap.equal_range(pObj->usItem); - for (it = range.first; it != range.second; it++) + UINT16 attachmentId = it->second.attachmentIndex; + if (ItemIsLegal(attachmentId, TRUE)) + uiSlotFlag |= Item[attachmentId].nasAttachmentClass; + } + + for (UINT32 i = 0; i < gMAXLAUNCHABLES_READ; i++) + { + if (Launchable[i][1] == pObj->usItem) { - UINT16 attachmentId = it->second.attachmentIndex; + UINT16 attachmentId = Launchable[i][0]; if (ItemIsLegal(attachmentId, TRUE)) uiSlotFlag |= Item[attachmentId].nasAttachmentClass; } - - for (UINT32 i = 0; i < gMAXLAUNCHABLES_READ; i++) - { - if (Launchable[i][1] == pObj->usItem) - { - UINT16 attachmentId = Launchable[i][0]; - if (ItemIsLegal(attachmentId, TRUE)) - uiSlotFlag |= Item[attachmentId].nasAttachmentClass; - } - } } - else + + UINT64 point = GetAvailableAttachmentPoint(pObj, 0); + for (UINT32 itemId = 1; itemId < gMAXITEMS_READ; itemId++) { - UINT64 point = GetAvailableAttachmentPoint(pObj, 0); - for (UINT32 itemId = 1; itemId < gMAXITEMS_READ; itemId++) - { - if (ItemIsLegal(itemId, TRUE) && IsAttachmentPointAvailable(point, itemId, TRUE)) - uiSlotFlag |= Item[itemId].nasAttachmentClass; - } + if (IsAttachmentPointAvailable(point, itemId, TRUE)) + uiSlotFlag |= Item[itemId].nasAttachmentClass; } return uiSlotFlag; From 4ebae982dd194bb26e9a94d9f344aca69c1f9364 Mon Sep 17 00:00:00 2001 From: Xiaoxun92 <32814679+Xiaoxun92@users.noreply.github.com> Date: Fri, 22 Sep 2023 18:10:08 -0700 Subject: [PATCH 21/24] Downgrade startup ini errors to warnings (#229) --- jascreens.cpp | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/jascreens.cpp b/jascreens.cpp index f921cb0bb..4409a0b45 100644 --- a/jascreens.cpp +++ b/jascreens.cpp @@ -398,7 +398,7 @@ UINT32 InitScreenHandle(void) int y = 40; #ifdef USE_VFS sgp::Logger_ID ini_id = sgp::Logger::instance().createLogger(); - sgp::Logger::instance().connectFile(ini_id, L"ERROR_REPORT.iniErrorMessages.txt", false, sgp::Logger::FLUSH_ON_DELETE); + sgp::Logger::instance().connectFile(ini_id, L"iniErrorReport.txt", false, sgp::Logger::FLUSH_ON_DELETE); sgp::Logger::LogInstance logger = sgp::Logger::instance().logger(ini_id); #endif while (! iniErrorMessages.empty()) { @@ -412,17 +412,17 @@ UINT32 InitScreenHandle(void) if (iniErrorMessage_create_out_file) { #ifndef USE_VFS - fopen_s( &file_pointer, "..\\ERROR_REPORT.iniErrorMessages.txt", "w" ); + fopen_s( &file_pointer, "..\\iniErrorReport.txt", "w" ); #endif y += 25; - swprintf( str, L"%S", "ERROR_REPORT.iniErrorMessages.txt has been created. Please review its content." ); - DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_RED, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); + swprintf( str, L"%S", "Warning: found the following ini errors. iniErrorReport.txt has been created." ); + DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_ORANGE, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); iniErrorMessage_create_out_file = FALSE; } else { #ifndef USE_VFS - fopen_s( &file_pointer, "..\\ERROR_REPORT.iniErrorMessages.txt", "a+" ); + fopen_s( &file_pointer, "..\\iniErrorReport.txt", "a+" ); #endif } @@ -434,10 +434,9 @@ UINT32 InitScreenHandle(void) #else logger << iniErrorMessage << sgp::endl; #endif - DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_RED, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); - iniErrorMessages.pop(); + DisplayWrappedString( 10, y, 560, 2, FONT12ARIAL, FONT_ORANGE, str, FONT_BLACK, TRUE, LEFT_JUSTIFIED ); - if (iniErrorMessages.empty()) {for(int x=0 ; x <= 65535*2 ; x++);} + iniErrorMessages.pop(); } InvalidateScreen( ); From 9033e1097a08fc48e03edf9df81ab4fdc053cf2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 2 Oct 2023 18:30:17 +0200 Subject: [PATCH 22/24] Radar and overhead map color variants (#219) --- GameSettings.cpp | 6 ++++ GameSettings.h | 6 ++++ TileEngine/Radar Screen.cpp | 32 +++++++++++++++++-- TileEngine/overhead map.cpp | 62 +++++++++++++++++++++++++++++++++++++ 4 files changed, 103 insertions(+), 3 deletions(-) diff --git a/GameSettings.cpp b/GameSettings.cpp index e81704493..00802dcba 100644 --- a/GameSettings.cpp +++ b/GameSettings.cpp @@ -1185,6 +1185,12 @@ void LoadGameExternalOptions() // Flugente: additional decals on objects (cracked walls, blood spatters etc.) gGameExternalOptions.fAdditionalDecals = iniReader.ReadBoolean( "Graphics Settings", "ADDITIONAL_DECALS", FALSE ); + // anv: map color variants + gGameExternalOptions.ubRadarMapModeDay = iniReader.ReadInteger("Graphics Settings", "RADAR_MAP_MODE_DAY", 0, 0, 2); + gGameExternalOptions.ubRadarMapModeNight = iniReader.ReadInteger("Graphics Settings", "RADAR_MAP_MODE_NIGHT", 3, 0, 3); + gGameExternalOptions.ubOverheadMapModeDay = iniReader.ReadInteger("Graphics Settings", "OVERHEAD_MAP_MODE_DAY", 0, 0, 2); + gGameExternalOptions.ubOverheadMapModeNight = iniReader.ReadInteger("Graphics Settings", "OVERHEAD_MAP_MODE_NIGHT", 0, 0, 3); + //################# Sound Settings ################# gGameExternalOptions.guiWeaponSoundEffectsVolume = iniReader.ReadInteger("Sound Settings","WEAPON_SOUND_EFFECTS_VOLUME", 0, 0, 1000 /*1000 = 10x?*/); diff --git a/GameSettings.h b/GameSettings.h index 33c87d168..b5df2be51 100644 --- a/GameSettings.h +++ b/GameSettings.h @@ -952,6 +952,12 @@ typedef struct BOOLEAN fAdditionalDecals; // Flugente: show additional decals on objects (cracked walls, blood spatters etc.) + // anv: map color variants + UINT8 ubRadarMapModeDay; + UINT8 ubRadarMapModeNight; + UINT8 ubOverheadMapModeDay; + UINT8 ubOverheadMapModeNight; + //enable ext mouse key BOOLEAN bAltAimEnabled; BOOLEAN bAimedBurstEnabled; diff --git a/TileEngine/Radar Screen.cpp b/TileEngine/Radar Screen.cpp index 6146818ee..c485d8ca2 100644 --- a/TileEngine/Radar Screen.cpp +++ b/TileEngine/Radar Screen.cpp @@ -167,9 +167,35 @@ BOOLEAN LoadRadarScreenBitmap(CHAR8 * aFilename ) if( GetVideoObject( &hVObject, gusRadarImage ) ) { - // ATE: Add a shade table! - hVObject->pShades[ 0 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 255, 255, 255, FALSE ); - hVObject->pShades[ 1 ] = Create16BPPPaletteShaded( hVObject->pPaletteEntry, 100, 100, 100, FALSE ); + // ATE: Add a shade table! + // anv: pShades[ 0 ] is a day radar map, pShades[ 1 ] is a night radar map + switch( gGameExternalOptions.ubRadarMapModeDay ) + { + case 0: + hVObject->pShades[0] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 255, 255, 255, FALSE); + break; + case 1: + hVObject->pShades[0] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 352, 352, 352, TRUE); + break; + case 2: + hVObject->pShades[0] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 160, 255, 160, TRUE); + break; + } + switch( gGameExternalOptions.ubRadarMapModeNight ) + { + case 0: + hVObject->pShades[1] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 255, 255, 255, FALSE); + break; + case 1: + hVObject->pShades[1] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 352, 352, 352, TRUE); + break; + case 2: + hVObject->pShades[1] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 160, 255, 160, TRUE); + break; + case 3: + hVObject->pShades[1] = Create16BPPPaletteShaded(hVObject->pPaletteEntry, 100, 100, 100, FALSE); + break; + } } } diff --git a/TileEngine/overhead map.cpp b/TileEngine/overhead map.cpp index c2b5a0528..313161c15 100644 --- a/TileEngine/overhead map.cpp +++ b/TileEngine/overhead map.cpp @@ -2299,6 +2299,68 @@ void CopyOverheadDBShadetablesFromTileset( ) } } + // anv: map color variants + if (NightTime()) + { + switch (gGameExternalOptions.ubOverheadMapModeNight) + { + case 0: + break; + case 1: + for (uiLoop = 0; uiLoop < (UINT32)giNumberOfTileTypes; uiLoop++) + { + for (uiLoop2 = 0; uiLoop2 < HVOBJECT_SHADE_TABLES; uiLoop2++) + { + gSmTileSurf[uiLoop].vo->pShades[uiLoop2] = Create16BPPPaletteShaded(gSmTileSurf[uiLoop].vo->pPaletteEntry, 352, 352, 352, TRUE); + } + } + break; + case 2: + for (uiLoop = 0; uiLoop < (UINT32)giNumberOfTileTypes; uiLoop++) + { + for (uiLoop2 = 0; uiLoop2 < HVOBJECT_SHADE_TABLES; uiLoop2++) + { + gSmTileSurf[uiLoop].vo->pShades[uiLoop2] = Create16BPPPaletteShaded(gSmTileSurf[uiLoop].vo->pPaletteEntry, 160, 255, 160, TRUE); + } + } + break; + case 3: + for (uiLoop = 0; uiLoop < (UINT32)giNumberOfTileTypes; uiLoop++) + { + for (uiLoop2 = 0; uiLoop2 < HVOBJECT_SHADE_TABLES; uiLoop2++) + { + gSmTileSurf[uiLoop].vo->pShades[uiLoop2] = Create16BPPPaletteShaded(gSmTileSurf[uiLoop].vo->pPaletteEntry, 100, 100, 100, FALSE); + } + } + break; + } + } + else + { + switch (gGameExternalOptions.ubOverheadMapModeDay) + { + case 0: + break; + case 1: + for (uiLoop = 0; uiLoop < (UINT32)giNumberOfTileTypes; uiLoop++) + { + for (uiLoop2 = 0; uiLoop2 < HVOBJECT_SHADE_TABLES; uiLoop2++) + { + gSmTileSurf[uiLoop].vo->pShades[uiLoop2] = Create16BPPPaletteShaded(gSmTileSurf[uiLoop].vo->pPaletteEntry, 352, 352, 352, TRUE); + } + } + break; + case 2: + for (uiLoop = 0; uiLoop < (UINT32)giNumberOfTileTypes; uiLoop++) + { + for (uiLoop2 = 0; uiLoop2 < HVOBJECT_SHADE_TABLES; uiLoop2++) + { + gSmTileSurf[uiLoop].vo->pShades[uiLoop2] = Create16BPPPaletteShaded(gSmTileSurf[uiLoop].vo->pPaletteEntry, 160, 255, 160, TRUE); + } + } + break; + } + } } void TrashOverheadMap( ) From a3be3ecd7e6132c6ac7c447d4edbf0c00211c95c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 2 Oct 2023 18:30:59 +0200 Subject: [PATCH 23/24] Specific drug types and drug items for background and facilities. (#226) --- Strategic/Campaign Types.h | 8 ++- Strategic/Facilities.cpp | 23 +++++- Strategic/Hourly Update.cpp | 122 ++++++++++++++++++++++++-------- Strategic/XML_FacilityTypes.cpp | 32 ++++++++- Tactical/Interface.h | 6 ++ Tactical/Soldier Control.cpp | 18 +++++ Tactical/Soldier Control.h | 4 ++ Tactical/XML.h | 5 +- Tactical/XML_Background.cpp | 50 +++++++++++-- 9 files changed, 227 insertions(+), 41 deletions(-) diff --git a/Strategic/Campaign Types.h b/Strategic/Campaign Types.h index af25aa9e8..37be5a191 100644 --- a/Strategic/Campaign Types.h +++ b/Strategic/Campaign Types.h @@ -3,6 +3,7 @@ #include "types.h" #include "DEBUG.H" +#include const int MAXIMUM_VALID_X_COORDINATE = 16; const int MINIMUM_VALID_X_COORDINATE = 1; @@ -223,6 +224,11 @@ enum NUM_RISKS, }; +enum class FacilityRiskVectorTypes +{ + RISK_DRUG_ITEMS, +}; + typedef struct FACILITYRISKTYPE { // The risks involved with perfoming an assignment at a specific facility. @@ -231,6 +237,7 @@ typedef struct FACILITYRISKTYPE INT8 bBaseEffect; // Base result. If negative, result will always be negative. If positive, result will always be positive. // If 0, result can be either negative or positive. UINT8 ubRange; // Range of deviation for the base effect. + std::map> valueVectors; // Optional additional data } FACILITYRISKTYPE; @@ -333,7 +340,6 @@ typedef struct FACILITYTYPE std::vector ProductionData; } FACILITYTYPE; -#define FACILITYTYPE_SIZEOF_POD offsetof(FACILITYTYPE, ProductionData) // HEADROCK HAM 3.5: Maximum number of different facility types #define MAX_NUM_FACILITY_TYPES 255 diff --git a/Strategic/Facilities.cpp b/Strategic/Facilities.cpp index cf039a447..52975d1a8 100644 --- a/Strategic/Facilities.cpp +++ b/Strategic/Facilities.cpp @@ -34,6 +34,7 @@ #include "Isometric Utils.h" #include "MilitiaSquads.h" #include "Tactical Save.h" +#include INT16 gsSkyriderCostModifier; // HEADROCK HAM 3.6: Strategic info variable, total of income/costs accumulated for the use of facilities today. @@ -1808,9 +1809,25 @@ void HandleRisksForSoldierFacilityAssignment( SOLDIERTYPE *pSoldier, UINT8 ubFac } // Add effects - CreateItem( ALCOHOL, Item[ALCOHOL].usPortionSize, &gTempObject ); - - ApplyConsumable( pSoldier, &gTempObject, TRUE, FALSE ); + { + std::vector& riskDrugItems = + gFacilityTypes[ubFacilityType].AssignmentData[ubAssignmentType].Risk[iCounter].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS]; + + if (riskDrugItems.empty()) + { + CreateItem(ALCOHOL, Item[ALCOHOL].usPortionSize, &gTempObject); + + ApplyConsumable(pSoldier, &gTempObject, TRUE, FALSE); + } + else + { + INT16 sItemId = riskDrugItems[std::uniform_int_distribution<>(0, riskDrugItems.size() - 1)(std::mt19937{ std::random_device{}() })]; + + CreateItem(sItemId, Item[sItemId].usPortionSize, &gTempObject); + + ApplyConsumable(pSoldier, &gTempObject, TRUE, FALSE); + } + } //pSoldier->AddDrugValues( DRUG_TYPE_ALCOHOL, Drug[DRUG_TYPE_ALCOHOL].ubDrugEffect, Drug[DRUG_TYPE_ALCOHOL].ubDrugTravelRate, Drug[DRUG_TYPE_ALCOHOL].ubDrugSideEffect ); diff --git a/Strategic/Hourly Update.cpp b/Strategic/Hourly Update.cpp index d9e0ddfaa..4cd628c26 100644 --- a/Strategic/Hourly Update.cpp +++ b/Strategic/Hourly Update.cpp @@ -39,8 +39,8 @@ // anv: for hourly heli repair #include "Vehicles.h" #include "Map Screen Helicopter.h" -// anv: transition to save screen in extreme iron man mode -#include "gameloop.h" +#include "gameloop.h" // anv: transition to save screen in extreme iron man mode +#include // anv: shuffle drug items in hourly update void HourlyQuestUpdate(); void HourlyLarryUpdate(); @@ -342,6 +342,7 @@ void HourlyQuestUpdate() } #define BAR_TEMPTATION 4 +#define INVENTORY_DRUG_TEMPTATION 5 // Flugente: abandoned the LarryItems for the new drug system /*#define NUM_LARRY_ITEMS 6 @@ -377,40 +378,105 @@ void HourlyLarryUpdate() { fTookDrugs = FALSE; - if ( pSoldier->bAssignment < ON_DUTY && !pSoldier->flags.fBetweenSectors && pSoldier->bInSector && !( gTacticalStatus.fEnemyInSector || guiCurrentScreen == GAME_SCREEN ) ) + const std::vector drugItems = pSoldier->GetBackgroundValueVector(BackgroundVectorTypes::BG_DRUGUSE_ITEMS); + const std::vector drugTypes = pSoldier->GetBackgroundValueVector(BackgroundVectorTypes::BG_DRUGUSE_TYPES); + + if ( pSoldier->bAssignment < ON_DUTY && !pSoldier->flags.fBetweenSectors && !( gTacticalStatus.fEnemyInSector || guiCurrentScreen == GAME_SCREEN ) ) { // Flugente: reworked this for the new drug system. We now loop over our entire inventory INT8 invsize = (INT8)pSoldier->inv.size(); // remember inventorysize, so we don't call size() repeatedly for ( INT8 bLoop = 0; bLoop < invsize; ++bLoop) // ... for all items in our inventory ... { - if ( pSoldier->inv[bLoop].exists() && Item[ pSoldier->inv[bLoop].usItem ].drugtype > 0 ) + if (pSoldier->inv[bLoop].exists()) { - pObj = &(pSoldier->inv[bLoop]); - - usTemptation = 5; - - // any drug will do... I'm not going to create a new tag for sth minor like this - break; + INT16 sCurrentItemId = pSoldier->inv[bLoop].usItem; + INT16 sCurrentDrugType = Item[sCurrentItemId].drugtype; + if (Item[sCurrentItemId].drugtype > 0 && Item[sCurrentItemId].usItemClass & (IC_KIT | IC_MISC)) + { + // anv: if drug user has no items or types specified, assume any is valid, otherwise check drug item id or type + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sCurrentItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), sCurrentDrugType) != drugTypes.end())) + { + pObj = &(pSoldier->inv[bLoop]); + usTemptation = INVENTORY_DRUG_TEMPTATION; + break; + } + } } } // check to see if we're in a bar sector, if we are, we have access to alcohol // which may be better than anything we've got... - if ( usTemptation < BAR_TEMPTATION && GetCurrentBalance() >= Item[ ALCOHOL ].usPrice ) + INT16 sBarDrugItemId = 0; + if ( usTemptation < BAR_TEMPTATION ) { - if ( pSoldier->bSectorZ == 0 && - ( ( pSoldier->sSectorX == 13 && pSoldier->sSectorY == MAP_ROW_D) || - ( pSoldier->sSectorX == 13 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 5 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 6 && pSoldier->sSectorY == MAP_ROW_C) || - ( pSoldier->sSectorX == 5 && pSoldier->sSectorY == MAP_ROW_D) || - ( pSoldier->sSectorX == 2 && pSoldier->sSectorY == MAP_ROW_H) - ) - ) + // sevenfm: check facility + if (pSoldier->bSectorZ == 0) { - // in a bar! - fBar = TRUE; - usTemptation = BAR_TEMPTATION; + for (UINT16 usFacilityType = 0; usFacilityType < NUM_FACILITY_TYPES; usFacilityType++) + { + // Is this facility here? + if (gFacilityLocations[SECTOR(pSoldier->sSectorX, pSoldier->sSectorY)][usFacilityType].fFacilityHere) + { + for (UINT16 usFacilityAssignment = 0; usFacilityAssignment < NUM_FACILITY_ASSIGNMENTS; usFacilityAssignment++) + { + // is it a place with risk of getting drunk? + if (gFacilityTypes[usFacilityType].AssignmentData[usFacilityAssignment].Risk[RISK_DRUNK].usChance > 0) + { + // anv: check all items available for this risk + const std::vector& riskDrugItems = + gFacilityTypes[usFacilityType].AssignmentData[usFacilityAssignment].Risk[RISK_DRUNK].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS]; + + if (riskDrugItems.empty()) + { + if (GetCurrentBalance() >= Item[ALCOHOL].usPrice) + { + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sBarDrugItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), Item[ALCOHOL].drugtype) != drugTypes.end())) + { + sBarDrugItemId = ALCOHOL; + + // Cool. + fBar = TRUE; + } + } + } + else + { + std::vector shuffledRiskDrugItems = riskDrugItems; + std::shuffle(shuffledRiskDrugItems.begin(), shuffledRiskDrugItems.end(), std::mt19937{ std::random_device{}() }); + for (INT16 sItemId : shuffledRiskDrugItems) + { + if (sItemId < MAXITEMS && Item[sItemId].usItemClass & (IC_KIT | IC_MISC) && GetCurrentBalance() >= Item[sItemId].usPrice) + { + if ((drugItems.empty() && drugTypes.empty()) || + (!drugItems.empty() && std::find(drugItems.begin(), drugItems.end(), sItemId) != drugItems.end()) || + (!drugTypes.empty() && std::find(drugTypes.begin(), drugTypes.end(), Item[sItemId].drugtype) != drugTypes.end())) + { + sBarDrugItemId = sItemId; + + // Cool. + fBar = TRUE; + + // sevenfm: stop searching + break; + } + } + } + } + } + + + if (fBar) + { + usTemptation = BAR_TEMPTATION; + break; + } + } + } + } } } @@ -473,10 +539,10 @@ void HourlyLarryUpdate() bBoozeSlot = FindEmptySlotWithin( pSoldier, HANDPOS, NUM_INV_SLOTS ); if ( bBoozeSlot != NO_SLOT ) { + INVTYPE drugItem = Item[sBarDrugItemId]; // take $ from player's account // silversurfer: changed the price to reflect the changed amount of 25% below - //usCashAmount = Item[ ALCOHOL ].usPrice; - usCashAmount = (UINT16)( Item[ALCOHOL].usPrice / 4.0f ); + usCashAmount = (UINT16)(drugItem.usPrice / 4.0f); AddTransactionToPlayersBook ( TRANSFER_FUNDS_TO_MERC, pSoldier->ubProfile, GetWorldTotalMin(), -( usCashAmount ) ); // give Larry booze here @@ -484,10 +550,10 @@ void HourlyLarryUpdate() // Now the bottle will be fully consumed below and vanishes from inventory before the player even gets to see it. This simulates going to a bar to have a drink there. UINT8 portionsize = 25; - if ( Item[ALCOHOL].usPortionSize > 0 && Item[ALCOHOL].usPortionSize < 25 ) - portionsize = Item[ALCOHOL].usPortionSize; + if (drugItem.usPortionSize > 0 && drugItem.usPortionSize < 25) + portionsize = drugItem.usPortionSize; - CreateItem( ALCOHOL, portionsize, &( pSoldier->inv[bBoozeSlot] ) ); + CreateItem( sBarDrugItemId, portionsize, &( pSoldier->inv[bBoozeSlot] ) ); ApplyConsumable( pSoldier, &( pSoldier->inv[bBoozeSlot] ), TRUE, FALSE ); } diff --git a/Strategic/XML_FacilityTypes.cpp b/Strategic/XML_FacilityTypes.cpp index 33468d388..598cf88c4 100644 --- a/Strategic/XML_FacilityTypes.cpp +++ b/Strategic/XML_FacilityTypes.cpp @@ -41,6 +41,7 @@ typedef enum typedef struct { FACILITYTYPE_PARSE_STAGE curElement; + PARSE_STAGE curGenericElement; CHAR8 szCharData[MAX_CHAR_DATA_LENGTH+1]; INT16 curAssignmentType; INT16 curRisk; @@ -168,7 +169,7 @@ facilitytypeStartElementHandle(void *userData, const XML_Char *name, const XML_C pData->curElement = FACILITYTYPE_TYPE; // Set all values to default before applying XML data - memset(&pData->curFacilityTypeData, 0, FACILITYTYPE_SIZEOF_POD); + pData->curFacilityTypeData = FACILITYTYPE(); InitFacilityTypeEntry( pData ); //DebugMsg(TOPIC_JA2, DBG_LEVEL_3,"MergeStartElementHandle: setting memory for curMerge"); @@ -405,6 +406,23 @@ facilitytypeStartElementHandle(void *userData, const XML_Char *name, const XML_C pData->maxReadDepth++; } + else if (pData->curElement == FACILITYTYPE_RISK && + pData->curRisk == RISK_DRUNK && + strcmp(name, "drugitems") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curElement = FACILITYTYPE_RISK_ELEMENT; + pData->curAssignmentData.Risk[pData->curRisk].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS].clear(); + + pData->maxReadDepth++; + } + else if (pData->curGenericElement == ELEMENT_VECTOR_OF_NUMBERS && + strcmp(name, "drugitem") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS_NUMBER; + pData->maxReadDepth++; + } + else if ( pData->curElement == FACILITYTYPE_PRODUCTION && ( strcmp( name, "szProductionName" ) == 0 || strcmp( name, "szAdditionalRequirementTips" ) == 0 || @@ -530,6 +548,7 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].usChance = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].usChance; gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].bBaseEffect = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].bBaseEffect; gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].ubRange = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].ubRange; + gFacilityTypes[pData->curIndex].AssignmentData[cnt].Risk[cntB].valueVectors = pData->curFacilityTypeData.AssignmentData[cnt].Risk[cntB].valueVectors; } } @@ -673,6 +692,7 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].usChance = pData->curAssignmentData.Risk[cnt].usChance; pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].bBaseEffect = pData->curAssignmentData.Risk[cnt].bBaseEffect; pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].ubRange = pData->curAssignmentData.Risk[cnt].ubRange; + pData->curFacilityTypeData.AssignmentData[pData->curAssignmentType].Risk[cnt].valueVectors = pData->curAssignmentData.Risk[cnt].valueVectors; } } else @@ -1252,6 +1272,14 @@ facilitytypeEndElementHandle(void *userData, const XML_Char *name) pData->curProductionData.usOptional_PreProducts.push_back( data ); } } + else if (pData->curGenericElement == ELEMENT_VECTOR_OF_NUMBERS_NUMBER && strcmp(name, "drugitem") == 0) + { + pData->curGenericElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curElement = FACILITYTYPE_RISK; + + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curAssignmentData.Risk[pData->curRisk].valueVectors[FacilityRiskVectorTypes::RISK_DRUG_ITEMS].push_back(drugValue); + } pData->maxReadDepth--; } @@ -1299,7 +1327,7 @@ BOOLEAN ReadInFacilityTypes(STR fileName, BOOLEAN localizedVersion) XML_SetCharacterDataHandler(parser, facilitytypeCharacterDataHandle); - memset(&pData, 0, FACILITYTYPEPARSEDATA_SIZE_OF_POD); + pData = facilitytypeParseData(); pData.maxArraySize = MAXITEMS; pData.curIndex = 0; diff --git a/Tactical/Interface.h b/Tactical/Interface.h index ff767245b..0df8a4c64 100644 --- a/Tactical/Interface.h +++ b/Tactical/Interface.h @@ -211,6 +211,11 @@ enum { BG_MAX, }; +enum class BackgroundVectorTypes { + BG_DRUGUSE_TYPES, + BG_DRUGUSE_ITEMS, +}; + typedef struct { UINT16 uiIndex; @@ -220,6 +225,7 @@ typedef struct UINT64 uiFlags; // this flagmask defines what special properties this background has (on/off behaviour) INT16 value[BG_MAX]; // property values + std::map> valueVectors; // optional additional data } BACKGROUND_VALUES; #define NUM_BACKGROUND 500 diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 271368d04..103ceb66d 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -17605,6 +17605,24 @@ INT16 SOLDIERTYPE::GetBackgroundValue( UINT16 aNr ) return 0; } +const std::vector& SOLDIERTYPE::GetBackgroundValueVector(BackgroundVectorTypes backgroundVectorType) const +{ + static const std::vector emptyVector; + + if (UsingBackGroundSystem() && this->ubProfile != NO_PROFILE) + { + const BACKGROUND_VALUES& background = zBackground[gMercProfiles[this->ubProfile].usBackground]; + auto iterator = background.valueVectors.find(backgroundVectorType); + + if (iterator != background.valueVectors.end()) + { + return iterator->second; + } + } + + return emptyVector; +} + INT8 SOLDIERTYPE::GetSuppressionResistanceBonus( ) { INT8 bonus = 0; diff --git a/Tactical/Soldier Control.h b/Tactical/Soldier Control.h index 697456188..5873618d4 100644 --- a/Tactical/Soldier Control.h +++ b/Tactical/Soldier Control.h @@ -1070,6 +1070,8 @@ public: INT8 bPathStored; // good for AI to reduct redundancy }; +enum class BackgroundVectorTypes; + class SOLDIERTYPE//last edited at version 102 { public: @@ -1954,6 +1956,8 @@ public: BOOLEAN HasBackgroundFlag( UINT64 aFlag ); INT16 GetBackgroundValue( UINT16 aNr ); + const std::vector& SOLDIERTYPE::GetBackgroundValueVector(BackgroundVectorTypes backgroundVectorType) const; + INT8 GetSuppressionResistanceBonus(); // bonus to resistance against suppression INT16 GetMeleeDamageBonus(); INT16 GetAPBonus(); diff --git a/Tactical/XML.h b/Tactical/XML.h index 03a958119..efa28c25b 100644 --- a/Tactical/XML.h +++ b/Tactical/XML.h @@ -44,7 +44,10 @@ enum ELEMENT_DISABILITY_EFFECT, ELEMENT_DISABILITY_EFFECT_PROPERTY, ELEMENT_PERSONALITY_EFFECT, - ELEMENT_PERSONALITY_EFFECT_PROPERTY + ELEMENT_PERSONALITY_EFFECT_PROPERTY, + + ELEMENT_VECTOR_OF_NUMBERS, + ELEMENT_VECTOR_OF_NUMBERS_NUMBER, } typedef PARSE_STAGE; diff --git a/Tactical/XML_Background.cpp b/Tactical/XML_Background.cpp index 347f14428..5caac1cde 100644 --- a/Tactical/XML_Background.cpp +++ b/Tactical/XML_Background.cpp @@ -41,8 +41,13 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha { pData->curElement = ELEMENT_LIST; - if ( !localizedTextOnly_BG ) - memset(pData->curArray,0,sizeof(BACKGROUND_VALUES)*pData->maxArraySize); + if (!localizedTextOnly_BG) + { + for (UINT32 i = 0; i < pData->maxArraySize; i++) + { + pData->curArray[i] = BACKGROUND_VALUES(); + } + } pData->maxReadDepth++; //we are not skipping this element } @@ -50,8 +55,8 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha { pData->curElement = ELEMENT; - if ( !localizedTextOnly_BG ) - memset(&pData->curBackground,0,sizeof(BACKGROUND_VALUES)); + if (!localizedTextOnly_BG) + pData->curBackground = BACKGROUND_VALUES(); pData->maxReadDepth++; //we are not skipping this element } @@ -153,6 +158,27 @@ backgroundStartElementHandle(void *userData, const XML_Char *name, const XML_Cha pData->maxReadDepth++; //we are not skipping this element } + else if (strcmp(name, "drugtypes") == 0 && pData->curElement == ELEMENT) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].clear(); + + pData->maxReadDepth++; //we are not skipping this element + } + else if (strcmp(name, "drugitems") == 0 && pData->curElement == ELEMENT) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].clear(); + + pData->maxReadDepth++; //we are not skipping this element + } + else if (pData->curElement == ELEMENT_VECTOR_OF_NUMBERS && + (strcmp(name, "drugtype") == 0 || + strcmp(name, "drugitem") == 0)) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS_NUMBER; + pData->maxReadDepth++; //we are not skipping this element + } pData->szCharData[0] = '\0'; } @@ -667,7 +693,19 @@ backgroundEndElementHandle(void *userData, const XML_Char *name) pData->curElement = ELEMENT; pData->curBackground.uiFlags |= (UINT16)atol(pData->szCharData) ? BACKGROUND_CIVGROUPLOYAL : 0; } - + else if (strcmp(name, "drugtype") == 0) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_TYPES].push_back(drugValue); + } + else if (strcmp(name, "drugitem") == 0) + { + pData->curElement = ELEMENT_VECTOR_OF_NUMBERS; + INT16 drugValue = (INT16)atol(pData->szCharData); + pData->curBackground.valueVectors[BackgroundVectorTypes::BG_DRUGUSE_ITEMS].push_back(drugValue); + } + pData->maxReadDepth--; } pData->currentDepth--; @@ -711,7 +749,7 @@ BOOLEAN ReadInBackgrounds(STR fileName, BOOLEAN localizedVersion) XML_SetCharacterDataHandler(parser, backgroundCharacterDataHandle); - memset(&pData,0,sizeof(pData)); + pData = enemyRankParseData(); pData.curArray = zBackground; pData.maxArraySize = NUM_BACKGROUND; From 14ec138fc7348c9cf6b0fbef22d178729c52e304 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andrzej=20Fa=C5=82kowski?= Date: Mon, 2 Oct 2023 18:32:15 +0200 Subject: [PATCH 24/24] Fixed UI movement mode not switching to crawl on character collapse, leading to deadlocks (#231) --- Tactical/Points.cpp | 2 +- Tactical/Soldier Control.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Tactical/Points.cpp b/Tactical/Points.cpp index f16aacc88..81597a60d 100644 --- a/Tactical/Points.cpp +++ b/Tactical/Points.cpp @@ -3214,7 +3214,7 @@ BOOLEAN CheckForMercContMove( SOLDIERTYPE *pSoldier ) return( FALSE ); } - if( pSoldier->stats.bLife >= OKLIFE ) + if( pSoldier->stats.bLife >= OKLIFE && !(pSoldier->bCollapsed && pSoldier->bBreath < OKBREATH) ) { if( pSoldier->sGridNo != pSoldier->pathing.sFinalDestination || pSoldier->bGoodContPath ) { diff --git a/Tactical/Soldier Control.cpp b/Tactical/Soldier Control.cpp index 103ceb66d..d41c63a4f 100644 --- a/Tactical/Soldier Control.cpp +++ b/Tactical/Soldier Control.cpp @@ -21855,6 +21855,8 @@ void SoldierCollapse( SOLDIERTYPE *pSoldier ) pSoldier->bCollapsed = TRUE; + pSoldier->usUIMovementMode = CRAWLING; + pSoldier->ReceivingSoldierCancelServices( ); // CC has requested - handle sight here...