Skip to content

Commit 946146f

Browse files
author
web3blind
committed
Fix player-reported gameplay UX issues
1 parent 5f2b396 commit 946146f

17 files changed

Lines changed: 427 additions & 25 deletions

File tree

app/js/data/quests.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ var GameQuests = (function() {
8383
minLevel: 3,
8484
maxLevel: 50,
8585
objectives: [
86-
{ type: 'explore', required: 3 }
86+
{ type: 'explore', required: 3, uniqueTarget: true }
8787
],
8888
rewards: {
8989
xp: 250,
@@ -100,7 +100,7 @@ var GameQuests = (function() {
100100
minLevel: 1,
101101
maxLevel: 50,
102102
objectives: [
103-
{ type: 'social', target: 'blessing', required: 5 }
103+
{ type: 'social', target: 'blessing', required: 5, uniqueTarget: true }
104104
],
105105
rewards: {
106106
xp: 200,

app/js/engine/quest-system.js

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ var QuestSystem = (function() {
7777
target: obj.target || '',
7878
required: obj.required,
7979
current: 0,
80-
completed: false
80+
completed: false,
81+
uniqueTarget: !!obj.uniqueTarget,
82+
seenTargets: []
8183
});
8284
}
8385

@@ -113,7 +115,28 @@ var QuestSystem = (function() {
113115
continue;
114116
}
115117

116-
obj.current += (eventData.count || 1);
118+
if (obj.uniqueTarget) {
119+
if (!eventData || !eventData.uniqueKey) {
120+
continue;
121+
}
122+
if (!obj.seenTargets) {
123+
obj.seenTargets = [];
124+
}
125+
var alreadySeen = false;
126+
for (var st = 0; st < obj.seenTargets.length; st++) {
127+
if (obj.seenTargets[st] === eventData.uniqueKey) {
128+
alreadySeen = true;
129+
break;
130+
}
131+
}
132+
if (alreadySeen) {
133+
continue;
134+
}
135+
obj.seenTargets.push(eventData.uniqueKey);
136+
obj.current = obj.seenTargets.length;
137+
} else {
138+
obj.current += (eventData.count || 1);
139+
}
117140
if (obj.current >= obj.required) {
118141
obj.current = obj.required;
119142
obj.completed = true;

app/js/engine/state-engine.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -597,7 +597,7 @@ var StateEngine = (function() {
597597

598598
if (typeof QuestSystem !== 'undefined' && data.zone && data.zone !== previousZone) {
599599
_ensureQuests(sender);
600-
QuestSystem.updateQuestProgress(worldState.quests[sender], 'explore', { target: data.zone, count: 1 });
600+
QuestSystem.updateQuestProgress(worldState.quests[sender], 'explore', { target: data.zone, uniqueKey: data.zone, count: 1 });
601601
}
602602

603603
return [{
@@ -631,7 +631,7 @@ var StateEngine = (function() {
631631
});
632632

633633
if (typeof QuestSystem !== 'undefined' && worldState.quests && worldState.quests[award.initiator]) {
634-
QuestSystem.updateQuestProgress(worldState.quests[award.initiator], 'social', { target: 'blessing', count: 1 });
634+
QuestSystem.updateQuestProgress(worldState.quests[award.initiator], 'social', { target: 'blessing', uniqueKey: award.receiver, count: 1 });
635635
}
636636
}
637637

app/js/i18n/en.js

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,8 @@ var LangEN = {
119119
char_intellect: 'Intellect',
120120
char_fortune: 'Fortune',
121121
char_core: 'Magic Core',
122+
char_stats_growth_hint: 'Stats grow from level, Core power, equipment, enchantments, and rare trophies. To improve one specific stat, find or craft items with that stat in the bag and workshop.',
123+
char_stats_growth_hint: 'Stats grow from level, Core power, equipment, enchantments, and rare trophies. To improve one specific stat, find or craft items with that stat in the bag and workshop.',
122124
char_core_power: 'Core Power',
123125
char_core_per_stat: 'Core Power / 5: +{value} to each main stat',
124126
char_mana_regen: 'Mana Regeneration',
@@ -145,7 +147,8 @@ var LangEN = {
145147
hunt_choose_creature: 'Choose your prey',
146148
hunt_no_creatures_here: 'There are currently no huntable creatures in this location.',
147149
hunt_no_creatures_hint: 'Open the map and return to The Commons of First Light, or wait until creatures appear in this region.',
148-
hunt_return_to_commons: 'Return to First Light',
150+
hunt_return_to_commons: 'Return to Commons of First Light',
151+
hunt_returned_to_commons: 'You returned to the Commons of First Light. Hunt is available again.',
149152
hunt_choose_spell: 'Choose a spell',
150153
hunt_attack: 'Attack',
151154
hunt_victory: 'Victory!',
@@ -165,8 +168,8 @@ var LangEN = {
165168
hunt_mana_cost: 'Costs {cost} Mana',
166169
hunt_not_enough_mana: 'Not enough Mana. Wait for regeneration.',
167170
hunt_armageddon_title: 'Armageddon',
168-
hunt_armageddon_desc: 'Consume 100% Mana for 100x XP. Requires Armageddon Stone artifact.',
169-
hunt_armageddon_no_stone: 'You need an Armageddon Stone to use this ability.',
171+
hunt_armageddon_desc: 'Consume 100% Mana for 100x XP. The Armageddon Stone is consumed on launch.',
172+
hunt_armageddon_no_stone: 'Find an Armageddon Stone as a rare Thornvine (Lv5+) drop or craft it at level 10 from Echo Shards, Shadow Shard, and Fire Dust.',
170173
hunt_armageddon_no_mana: 'Armageddon requires full Mana (100%). Wait for full regeneration.',
171174
hunt_armageddon_confirm: 'I understand that after using Armageddon I will not be able to play until my Mana fully regenerates (~5 days).',
172175
hunt_armageddon_launch: 'UNLEASH ARMAGEDDON !!!',
@@ -590,6 +593,7 @@ var LangEN = {
590593
craft_error_wrong_class: 'Wrong class.',
591594
craft_error_wrong_location: 'Wrong location.',
592595
craft_error_not_enough_mana: 'Not enough mana.',
596+
craft_required_mana: 'Required: {cost} Mana.',
593597
craft_error_missing_materials: 'Missing materials.',
594598
craft_error_unknown: 'Unknown error.',
595599

@@ -622,6 +626,8 @@ var LangEN = {
622626
enchant_select_item_first: 'Select an item first.',
623627
enchant_consumables: 'Consumables',
624628
consume_success: 'Item consumed!',
629+
consume_success_hp: 'Item consumed: health +{amount} HP. Now {current}/{max} HP.',
630+
consume_success_mana: 'Item consumed: Mana +{amount}. Now {current}.',
625631
consume_error: 'Failed to consume item.',
626632

627633
// Enchantment types

app/js/i18n/ru.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ var LangRU = {
119119
char_intellect: 'Дух',
120120
char_fortune: 'Удача',
121121
char_core: 'Магическое Ядро',
122+
char_stats_growth_hint: 'Характеристики растут от уровня, Ядра, экипировки, зачарований и редких трофеев. Если хочешь усилить конкретную черту, ищи или создавай предметы с этой характеристикой в сумке и мастерской.',
122123
char_core_power: 'Сила Ядра',
123124
char_core_per_stat: 'Сила Ядра / 5: +{value} к каждому основному стату',
124125
char_mana_regen: 'Регенерация Маны',
@@ -146,6 +147,7 @@ var LangRU = {
146147
hunt_no_creatures_here: 'В этой локации сейчас нет доступной добычи для охоты.',
147148
hunt_no_creatures_hint: 'Откройте карту и вернитесь в The Commons of First Light или дождитесь появления существ в этом регионе.',
148149
hunt_return_to_commons: 'Вернуться в First Light',
150+
hunt_returned_to_commons: 'Вы вернулись в Общины Первого Света. Охота снова доступна.',
149151
hunt_choose_spell: 'Выбери заклинание',
150152
hunt_attack: 'Атаковать',
151153
hunt_victory: 'Победа!',
@@ -165,8 +167,8 @@ var LangRU = {
165167
hunt_mana_cost: 'Стоит {cost} маны',
166168
hunt_not_enough_mana: 'Маны мало. Подожди восстановления.',
167169
hunt_armageddon_title: 'Армагеддон',
168-
hunt_armageddon_desc: 'Потратить 100% маны для получения 100x XP. Требует артефакт Камень Армагеддона.',
169-
hunt_armageddon_no_stone: 'Для этого нужен Камень Армагеддона.',
170+
hunt_armageddon_desc: 'Потратить 100% маны для получения 100x XP. Камень Армагеддона расходуется при запуске.',
171+
hunt_armageddon_no_stone: 'Камень Армагеддона можно получить редким дропом с Thornvine (Lv5+) или создать в Мастерской на 10 уровне из Echo Shards, Shadow Shard и Fire Dust.',
170172
hunt_armageddon_no_mana: 'Армагеддон требует полной маны (100%). Подождите полного восстановления.',
171173
hunt_armageddon_confirm: 'Я понимаю, что после запуска Армагеддона не смогу играть, пока мана не восстановится полностью (~5 дней).',
172174
hunt_armageddon_launch: 'UNLEASH ARMAGEDDON !!!',
@@ -590,6 +592,7 @@ var LangRU = {
590592
craft_error_wrong_class: 'Неподходящий класс.',
591593
craft_error_wrong_location: 'Нужно быть в другом месте.',
592594
craft_error_not_enough_mana: 'Мало маны.',
595+
craft_required_mana: 'Нужно: {cost} маны.',
593596
craft_error_missing_materials: 'Не хватает материалов.',
594597
craft_error_unknown: 'Неизвестная ошибка.',
595598

@@ -622,6 +625,8 @@ var LangRU = {
622625
enchant_select_item_first: 'Сначала выберите предмет.',
623626
enchant_consumables: 'Расходники',
624627
consume_success: 'Предмет использован!',
628+
consume_success_hp: 'Предмет использован: здоровье +{amount} HP. Сейчас {current}/{max} HP.',
629+
consume_success_mana: 'Предмет использован: мана +{amount}. Сейчас {current}.',
625630
consume_error: 'Не удалось использовать предмет.',
626631

627632
// Enchantment types

app/js/ui/app.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ var App = (function() {
5151
VizConnection.init(function(err) {
5252
if (err) {
5353
console.log('Connection failed, working offline');
54-
Toast.info(Helpers.t('conn_disconnected'));
54+
Toast.info(Helpers.t('conn_disconnected'), 4000, { key: 'conn_disconnected' });
5555
} else {
5656
console.log('Connected to VIZ network');
5757
_checkHistoryCapabilityNotice();
@@ -128,7 +128,7 @@ var App = (function() {
128128
Toast.success(Helpers.t('conn_connected'));
129129
});
130130
VizConnection.onDisconnect(function() {
131-
Toast.info(Helpers.t('conn_disconnected'));
131+
Toast.info(Helpers.t('conn_disconnected'), 4000, { key: 'conn_disconnected' });
132132
});
133133

134134
// Register PWA install
@@ -322,7 +322,7 @@ var App = (function() {
322322
if (err || !capability || capability.historicalBlocks) return;
323323
console.log('App: Selected VIZ node has limited history; archive mirror may be needed for old recovery');
324324
if (capability.recentBlocks && typeof Toast !== 'undefined') {
325-
Toast.info(Helpers.t('conn_history_limited'));
325+
Toast.info(Helpers.t('conn_history_limited'), 4000, { key: 'conn_history_limited' });
326326
}
327327
});
328328
}

app/js/ui/components/toast.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ var Toast = (function() {
55
'use strict';
66

77
var container = null;
8+
var activeKeys = {};
89

910
function _getContainer() {
1011
if (!container) {
@@ -28,8 +29,16 @@ var Toast = (function() {
2829
duration = duration || 3000;
2930
options = options || {};
3031

32+
if (options.key && activeKeys[options.key] && activeKeys[options.key].parentNode) {
33+
return activeKeys[options.key];
34+
}
35+
3136
var toast = document.createElement('div');
3237
toast.className = 'toast toast-' + type;
38+
if (options.key) {
39+
toast.setAttribute('data-toast-key', options.key);
40+
activeKeys[options.key] = toast;
41+
}
3342
toast.textContent = message;
3443
toast.setAttribute('role', 'alert');
3544

@@ -67,6 +76,9 @@ var Toast = (function() {
6776
toast.classList.remove('show');
6877
setTimeout(function() {
6978
if (toast.parentNode) toast.parentNode.removeChild(toast);
79+
if (options.key && activeKeys[options.key] === toast) {
80+
delete activeKeys[options.key];
81+
}
7082
}, 300);
7183
}, duration);
7284
}

app/js/ui/screens/character.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ var CharacterScreen = (function() {
3838
_statRow(t('char_intellect'), totalInt) +
3939
_statRow(t('char_fortune'), totalFor) +
4040
'</div>' +
41+
'<p class="quest-desc">' + t('char_stats_growth_hint') + '</p>' +
4142
'<h2>' + t('char_core') + '</h2>' +
4243
'<p>' + t('char_core_power') + ': ' + Helpers.formatNumber(ch.coreBonus) + '</p>' +
4344
'<p>' + t('char_core_per_stat', { value: corePerStat }) + '</p>' +

app/js/ui/screens/chronicle.js

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ var ChronicleScreen = (function() {
1111
var BLESS_MEMO_PREFIX = 'viz://vm/bless/';
1212
var currentTab = 'all'; // all, guild, friends, world
1313
var REQUIRED_TAG = '#viz_magic';
14+
var cachedFeedHtml = {};
1415

1516
function render() {
1617
var t = Helpers.t;
@@ -110,7 +111,12 @@ var ChronicleScreen = (function() {
110111
var feed = Helpers.$('chronicle-feed');
111112
if (!feed) return;
112113

113-
feed.innerHTML = '<p class="empty-state">' + Helpers.t('loading') + '</p>';
114+
if (cachedFeedHtml[currentTab]) {
115+
feed.innerHTML = cachedFeedHtml[currentTab];
116+
_bindBlessButtons(feed);
117+
} else {
118+
feed.innerHTML = '<p class="empty-state">' + Helpers.t('loading') + '</p>';
119+
}
114120

115121
var state = StateEngine.getState();
116122
var entries = _collectPostEntries(state);
@@ -269,6 +275,7 @@ var ChronicleScreen = (function() {
269275
if (entries.length === 0) {
270276
feed.innerHTML = '<p class="empty-state">' + Helpers.t('chronicle_empty') + '</p>' +
271277
'<p class="chronicle-hint">' + Helpers.t('chronicle_bless_posts_only_hint') + '</p>';
278+
cachedFeedHtml[currentTab] = feed.innerHTML;
272279
return;
273280
}
274281

@@ -280,7 +287,11 @@ var ChronicleScreen = (function() {
280287
html += _renderEntry(entries[k]);
281288
}
282289
feed.innerHTML = html;
290+
cachedFeedHtml[currentTab] = html;
291+
_bindBlessButtons(feed);
292+
}
283293

294+
function _bindBlessButtons(feed) {
284295
var blessBtns = feed.querySelectorAll('.bless-button');
285296
for (var m = 0; m < blessBtns.length; m++) {
286297
blessBtns[m].addEventListener('click', _onBless);
@@ -314,6 +325,23 @@ var ChronicleScreen = (function() {
314325
});
315326
}
316327

328+
function _injectLocalBlessing(account, energy) {
329+
var state = StateEngine.getState();
330+
var user = VizAccount.getCurrentUser();
331+
if (!state || !user || !account) return;
332+
state.recentActions.push({
333+
type: 'blessing_sent',
334+
sender: user,
335+
receiver: account,
336+
blockNum: (state.headBlock || 0) + 1,
337+
timestamp: Date.now(),
338+
energy: energy || BLESS_ENERGY_LOW,
339+
memo: BLESS_MEMO_PREFIX + account,
340+
events: []
341+
});
342+
cachedFeedHtml = {};
343+
}
344+
317345
function _renderEntry(entry) {
318346
var t = Helpers.t;
319347
var charInfo = StateEngine.getCharacter(entry.account);
@@ -365,8 +393,10 @@ var ChronicleScreen = (function() {
365393
if (err) {
366394
Toast.error(Helpers.t('error_low_mana'));
367395
} else {
396+
_injectLocalBlessing(account, energy);
368397
Toast.success('\u2728 ' + Helpers.t('chronicle_blessed'));
369398
SoundManager.play('bless_recv');
399+
_loadFeed();
370400
}
371401
});
372402
}

app/js/ui/screens/crafting.js

Lines changed: 30 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,11 @@ var CraftingScreen = (function() {
116116
html += '<span class="recipe-ready">' + t('craft_ready') + '</span>';
117117
} else {
118118
var errorKey = 'craft_error_' + (recipeInfo.error || 'unknown');
119-
html += '<span class="recipe-locked-text">' + t(errorKey) + '</span>';
119+
var errorText = t(errorKey);
120+
if (recipeInfo.error === 'not_enough_mana' && recipe.manaCost) {
121+
errorText += ' ' + t('craft_required_mana', { cost: Helpers.bpToPercent(recipe.manaCost) });
122+
}
123+
html += '<span class="recipe-locked-text">' + errorText + '</span>';
120124
}
121125
html += '</div>';
122126
html += '</div>';
@@ -461,7 +465,12 @@ var CraftingScreen = (function() {
461465
// Validate
462466
var validation = CraftingSystem.validateRecipe(selectedRecipe, character, inventory, character.currentZone || '');
463467
if (!validation.valid) {
464-
Toast.error(t('craft_error_' + validation.error));
468+
var validationMessage = t('craft_error_' + validation.error);
469+
var validationRecipe = GameRecipes.getRecipe(selectedRecipe);
470+
if (validation.error === 'not_enough_mana' && validationRecipe && validationRecipe.manaCost) {
471+
validationMessage += ' ' + t('craft_required_mana', { cost: Helpers.bpToPercent(validationRecipe.manaCost) });
472+
}
473+
Toast.error(validationMessage);
465474
SoundManager.play('error');
466475
return;
467476
}
@@ -630,6 +639,24 @@ var CraftingScreen = (function() {
630639
});
631640
}
632641

642+
function _consumeSuccessMessage(t, result, character) {
643+
var effect = result ? result.effect : null;
644+
if (effect && effect.type === 'hp_restore') {
645+
return t('consume_success_hp', {
646+
amount: effect.amount || 0,
647+
current: character.hp || 0,
648+
max: character.maxHp || 0
649+
});
650+
}
651+
if (effect && effect.type === 'mana_restore') {
652+
return t('consume_success_mana', {
653+
amount: Helpers.bpToPercent(effect.amount || 0),
654+
current: Helpers.bpToPercent(character.mana || 0)
655+
});
656+
}
657+
return t('consume_success');
658+
}
659+
633660
/**
634661
* Execute consume
635662
*/
@@ -654,7 +681,7 @@ var CraftingScreen = (function() {
654681
} else {
655682
var result = EnchantingSystem.consumeItem(item, character);
656683
if (result.success) {
657-
Toast.success(t('consume_success'));
684+
Toast.success(_consumeSuccessMessage(t, result, character));
658685
SoundManager.play('success');
659686
SoundManager.vibrate('light');
660687
}

0 commit comments

Comments
 (0)