Healing skills implementation in SSL, v2

- Got rid of duplicate code with one function instead of switch/case, while keeping logic (hopefully) unchanged
This commit is contained in:
phobos2077
2024-05-24 02:36:00 +02:00
parent c986298fe0
commit 18e976ded9
2 changed files with 124 additions and 258 deletions
@@ -1,5 +1,6 @@
/* /*
This script reimplements the healing skills of the original game. This script reimplements the healing skills of the original game.
This version uses a refactored code with no less code duplication, but potentially some minor differences from vanilla.
You can use this to make any tweaks you want to these skills. You can use this to make any tweaks you want to these skills.
*/ */
@@ -20,8 +21,7 @@
#define critter_max_hp(cr) get_critter_stat(cr, STAT_max_hit_points) #define critter_max_hp(cr) get_critter_stat(cr, STAT_max_hit_points)
#define critter_cur_hp(cr) get_critter_stat(cr, STAT_current_hp) #define critter_cur_hp(cr) get_critter_stat(cr, STAT_current_hp)
#define critter_body_type(cr) proto_data(obj_pid(cr), cr_body_type) #define critter_body_type(cr) proto_data(obj_pid(cr), cr_body_type)
#define critter_dmg_flags(cr) get_object_data(cr, OBJ_DATA_DAMAGE_FLAGS) #define critter_has_dmg_flag(cr, flag) ((get_object_data(cr, OBJ_DATA_DAMAGE_FLAGS) bwand flag) != 0)
#define critter_combat_flags(cr) get_object_data(cr, OBJ_DATA_COMBAT_STATE)
#define clear_critter_flag(cr, offset, flag) set_object_data(cr, offset, get_object_data(cr, offset) bwand bwnot(flag)) #define clear_critter_flag(cr, offset, flag) set_object_data(cr, offset, get_object_data(cr, offset) bwand bwnot(flag))
#define clear_critter_dmg_flag(cr, flag) clear_critter_flag(cr, OBJ_DATA_DAMAGE_FLAGS, flag) #define clear_critter_dmg_flag(cr, flag) clear_critter_flag(cr, OBJ_DATA_DAMAGE_FLAGS, flag)
@@ -35,6 +35,11 @@ variable times_skill_used, healable_damage_flags;
procedure skill_use_slot_available(variable skill) begin procedure skill_use_slot_available(variable skill) begin
variable slot, time, hoursSinceLastUsage, variable slot, time, hoursSinceLastUsage,
skillOffset := skill * SKILLS_MAX_USES_PER_DAY; skillOffset := skill * SKILLS_MAX_USES_PER_DAY;
// Lazy init array
if (not times_skill_used) then
times_skill_used := create_array_list(SKILL_COUNT * SKILLS_MAX_USES_PER_DAY);
for (slot := 0; slot < SKILLS_MAX_USES_PER_DAY; slot++) begin for (slot := 0; slot < SKILLS_MAX_USES_PER_DAY; slot++) begin
if (times_skill_used[skillOffset + slot] == 0) then if (times_skill_used[skillOffset + slot] == 0) then
return slot; return slot;
@@ -87,7 +92,7 @@ procedure critter_is_crippled(variable critter) begin
if (obj_type(critter) != OBJ_TYPE_CRITTER) then if (obj_type(critter) != OBJ_TYPE_CRITTER) then
return false; return false;
return (critter_dmg_flags(critter) bwand DAM_CRIP) != 0; return critter_has_dmg_flag(critter, DAM_CRIP);
end end
procedure get_skill_xp(variable skill) begin procedure get_skill_xp(variable skill) begin
@@ -99,7 +104,7 @@ procedure get_skill_xp(variable skill) begin
end end
end end
procedure show_skill_use_messages(variable skill, variable successCount) begin procedure show_skill_use_messages(variable skill, variable successCount := 1) begin
variable baseExp, xpToAdd, before, after; variable baseExp, xpToAdd, before, after;
if (successCount <= 0) then return; if (successCount <= 0) then return;
@@ -132,179 +137,25 @@ end
/** /**
* A partial implementation of skill_use function from the engine, with only healing-related code. Returns true if skill use was successful. * A refactored version of skill_use function from the engine, for use with healing skills only. Returns true if skill use was successful.
* @arg {ObjectPtr} user - Critter using the skill * @arg {ObjectPtr} user
* @arg {ObjectPtr} target - Skill use target * @arg {ObjectPtr} target
* @arg {int} skill - Skill num being used * @arg {int} skill - First Aid, Doctor or Repair
* @arg {int} skillBonus - Skill bonus/modifier from an item being used, such as First Aid Kit * @arg {int} skillBonus
* @ret {bool}
*/ */
procedure healing_skill_use_impl(variable user, variable target, variable skill, variable skillBonus) begin procedure use_healing_skill(variable user, variable target, variable skill, variable skillBonus) begin
variable giveExp, curHp, maxHp, hpToHeal, minHpToHeal, maxHpToHeal, critChance, healingAttempts, successCount, skillUseSlotAdded, roll, i, prefixMsg; variable curHp, maxHp, healingAttempts, isRobot, isRepair, isFirstAid;
//display_msg(string_format("user = %s, target = %s, bonus = %d, skill = %d", obj_name(user), obj_name(target), skillBonus, skill)); display_msg(string_format("use_healing_skill: user = %s, target = %s, bonus = %d, skill = %d", obj_name(user), obj_name(target), skillBonus, skill));
if (user == dude_obj and (skill == SKILL_FIRST_AID or skill == SKILL_DOCTOR)) then begin
variable healerRank := has_trait(TRAIT_PERK, user, PERK_healer);
minHpToHeal := 4 * healerRank;
maxHpToHeal := 10 * healerRank;
end
giveExp := true; isRobot := (critter_body_type(target) == CR_BODY_ROBOTIC);
curHp := critter_cur_hp(target); isRepair := (skill == SKILL_REPAIR);
maxHp := critter_max_hp(target); if (isRepair and not isRobot) then begin
critChance := get_critter_stat(user, STAT_crit_chance) + skillBonus;
healingAttempts := 1;
switch (skill) begin
case SKILL_FIRST_AID: begin
if (skill_use_slot_available(SKILL_FIRST_AID) == -1) then begin
// 590: You've taxed your ability with that skill. Wait a while.
// 591: You're too tired.
// 592: The strain might kill you.
display_msg(mstr_skill(590 + random(0, 2)));
return false;
end
if (critter_is_dead(target)) then begin
// 512: You can't heal the dead.
// 513: Let the dead rest in peace.
// 514: It's dead, get over it.
display_msg(mstr_skill(512 + random(0, 2)));
return false;
end
if (curHp < maxHp) then begin
fade_out;
roll := roll_vs_skill(user, skill, critChance)
if (critter_body_type(target) != CR_BODY_ROBOTIC)
else ROLL_FAILURE;
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin
hpToHeal = random(minHpToHeal + 1, maxHpToHeal + 5);
critter_heal(target, hpToHeal);
if (user == dude_obj) then begin
// You heal %d hit points.
display_msg(sprintf(mstr_skill(500), math_min(maxHp - curHp, hpToHeal)));
end
clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
call skill_use_slot_add(SKILL_FIRST_AID);
// debug skill usage
//display_msg(debug_array_str(array_slice(times_skill_used, skill * SKILLS_MAX_USES_PER_DAY, SKILLS_MAX_USES_PER_DAY)));
successCount := 1;
end else begin
// You fail to do any healing.
display_msg(sprintf(mstr_skill(503), how_much(0)));
end
fade_in;
end else if (user == dude_obj) then begin
// 501: You look healty already
// 502: %s looks healthy already
display_msg(mstr_skill(501)
if (target == dude_obj)
else sprintf(mstr_skill(502), obj_name(target)));
end
if (user == dude_obj) then begin
game_time_advance(30 * ONE_GAME_MINUTE);
end
end
case SKILL_DOCTOR: begin
if (skill_use_slot_available(SKILL_DOCTOR) == -1) then begin
// 590: You've taxed your ability with that skill. Wait a while.
// 591: You're too tired.
// 592: The strain might kill you.
display_msg(mstr_skill(590 + random(0, 2)));
return false;
end
if (critter_is_dead(target)) then begin
// 512: You can't heal the dead.
// 513: Let the dead rest in peace.
// 514: It's dead, get over it.
display_msg(mstr_skill(512 + random(0, 2)));
return false;
end
if (curHp < maxHp or critter_is_crippled(target)) then begin
fade_out;
if (critter_body_type(target) != CR_BODY_ROBOTIC and critter_is_crippled(target)) then begin
for (i := 0; i < len_array(healable_damage_flags); i++) begin
if ((critter_dmg_flags(target) bwand healable_damage_flags[i]) != 0) then begin
healingAttempts += 1;
roll := roll_vs_skill(user, skill, critChance);
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin
clear_critter_dmg_flag(target, healable_damage_flags[i]);
clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
// 520: You heal your %s.
// 521: You heal the %s.
prefixMsg := 520 if (target == dude_obj) else 521;
call skill_use_slot_add(SKILL_DOCTOR);
successCount := 1;
skillUseSlotAdded := true;
end else begin
// 525: You fail to heal your %s.
// 526: You fail to heal the %s.
prefixMsg := 525 if (target == dude_obj) else 526;
end
// 530: damaged eye
// 531: crippled left arm
// 532: crippled right arm
// 533: crippled right leg
// 534: crippled left leg
display_msg(sprintf(mstr_skill(prefixMsg), mstr_skill(530 + i)));
if (user == dude_obj) then
call show_skill_use_messages(skill, successCount);
giveExp := false;
end
end
end
roll := roll_vs_skill(user, skill, critChance)
if (critter_body_type(target) != CR_BODY_ROBOTIC)
else ROLL_FAILURE;
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin
hpToHeal = random(minHpToHeal + 4, maxHpToHeal + 10);
critter_heal(target, hpToHeal);
if (user == dude_obj) then begin
// You heal %d hit points.
display_msg(sprintf(mstr_skill(500), math_min(maxHp - curHp, hpToHeal)));
end
if (not skillUseSlotAdded) then
call skill_use_slot_add(SKILL_DOCTOR);
clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
successCount := 1;
if (user == dude_obj) then
call show_skill_use_messages(skill, successCount);
giveExp := false;
end else begin
// You fail to do any healing.
display_msg(sprintf(mstr_skill(503), how_much(0)));
end
fade_in;
end else if (user == dude_obj) then begin
// 501: You look healty already
// 502: %s looks healthy already
display_msg(mstr_skill(501)
if (target == dude_obj)
else sprintf(mstr_skill(502), obj_name(target)));
end
if (user == dude_obj) then begin
game_time_advance(ONE_GAME_HOUR * healingAttempts);
end
end
case SKILL_REPAIR: begin
if (critter_body_type(target) != CR_BODY_ROBOTIC) then begin
// You cannot repair that. // You cannot repair that.
display_msg(mstr_skill(553)); display_msg(mstr_skill(553));
return false; return false;
end end
if (skill_use_slot_available(SKILL_REPAIR) == -1) then begin if (skill_use_slot_available(skill) == -1) then begin
// 590: You've taxed your ability with that skill. Wait a while. // 590: You've taxed your ability with that skill. Wait a while.
// 591: You're too tired. // 591: You're too tired.
// 592: The strain might kill you. // 592: The strain might kill you.
@@ -312,16 +163,40 @@ procedure healing_skill_use_impl(variable user, variable target, variable skill,
return false; return false;
end end
if (critter_is_dead(target)) then begin if (critter_is_dead(target)) then begin
// You got it? // 512: You can't heal the dead.
display_msg(mstr_skill(1101)); // 513: Let the dead rest in peace.
// 514: It's dead, get over it.
display_msg(mstr_skill(512 + random(0, 2)));
return false; return false;
end end
if (curHp < maxHp or critter_is_crippled(target)) then begin
isFirstAid := (skill == SKILL_FIRST_AID);
curHp := critter_cur_hp(target);
maxHp := critter_max_hp(target);
healingAttempts := 1;
if (curHp < maxHp or (not isFirstAid and critter_is_crippled(target))) then begin
variable skillUseSlotAdded, roll, i, prefixMsg,
critChance := get_critter_stat(user, STAT_crit_chance) + skillBonus;
fade_out; fade_out;
// Healing of crippled limbs // Heal crippled limbs.
if (not isFirstAid and (not isRepair or not isRobot) and critter_is_crippled(target)) then begin
// Lazy init healable flags
if (not healable_damage_flags) then
healable_damage_flags := array_fixed([
DAM_BLIND,
DAM_CRIP_ARM_LEFT,
DAM_CRIP_ARM_RIGHT,
DAM_CRIP_LEG_RIGHT,
DAM_CRIP_LEG_LEFT
]);
for (i := 0; i < len_array(healable_damage_flags); i++) begin for (i := 0; i < len_array(healable_damage_flags); i++) begin
if ((critter_dmg_flags(target) bwand healable_damage_flags[i]) != 0) then begin if (not critter_has_dmg_flag(target, healable_damage_flags[i])) then
continue;
healingAttempts += 1; healingAttempts += 1;
roll := roll_vs_skill(user, skill, critChance); roll := roll_vs_skill(user, skill, critChance);
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin
@@ -330,8 +205,7 @@ procedure healing_skill_use_impl(variable user, variable target, variable skill,
// 520: You heal your %s. // 520: You heal your %s.
// 521: You heal the %s. // 521: You heal the %s.
prefixMsg := 520 if (target == dude_obj) else 521; prefixMsg := 520 if (target == dude_obj) else 521;
call skill_use_slot_add(SKILL_REPAIR); call skill_use_slot_add(skill);
successCount := 1;
skillUseSlotAdded := true; skillUseSlotAdded := true;
end else begin end else begin
// 525: You fail to heal your %s. // 525: You fail to heal your %s.
@@ -345,16 +219,26 @@ procedure healing_skill_use_impl(variable user, variable target, variable skill,
// 534: crippled left leg // 534: crippled left leg
display_msg(sprintf(mstr_skill(prefixMsg), mstr_skill(530 + i))); display_msg(sprintf(mstr_skill(prefixMsg), mstr_skill(530 + i)));
if (user == dude_obj) then if (user == dude_obj) then
call show_skill_use_messages(skill, successCount); call show_skill_use_messages(skill);
giveExp := false;
end end
end end
// For some reason, original code uses roll_check here, which is lower level function that doesn't take party member's skills into account, unlike roll_vs_skill (which calls into skill_result // Restore hit points.
roll := roll_check(has_skill(user, skill), critChance); roll := (roll_vs_skill(user, skill, critChance) if isFirstAid else roll_check(has_skill(user, skill), critChance))
if (isRepair or not isRobot)
else ROLL_FAILURE;
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin
hpToHeal = random(minHpToHeal + 4, maxHpToHeal + 10); variable
minHpToHeal := (1 if isFirstAid else 4),
maxHpToHeal := (5 if isFirstAid else 10),
hpToHeal;
if (user == dude_obj and not isRepair) then begin
variable healerRank := has_trait(TRAIT_PERK, user, PERK_healer);
minHpToHeal += 4 * healerRank;
maxHpToHeal += 10 * healerRank;
end
hpToHeal = random(minHpToHeal, maxHpToHeal);
critter_heal(target, hpToHeal); critter_heal(target, hpToHeal);
if (user == dude_obj) then begin if (user == dude_obj) then begin
@@ -363,13 +247,11 @@ procedure healing_skill_use_impl(variable user, variable target, variable skill,
end end
if (not skillUseSlotAdded) then if (not skillUseSlotAdded) then
call skill_use_slot_add(SKILL_REPAIR); call skill_use_slot_add(skill);
clear_critter_combat_flag(target, COMBAT_STATE_FLEEING); clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
successCount := 1;
if (user == dude_obj) then if (user == dude_obj) then
call show_skill_use_messages(skill, successCount); call show_skill_use_messages(skill);
giveExp := false;
end else begin end else begin
// You fail to do any healing. // You fail to do any healing.
display_msg(sprintf(mstr_skill(503), how_much(0))); display_msg(sprintf(mstr_skill(503), how_much(0)));
@@ -384,18 +266,10 @@ procedure healing_skill_use_impl(variable user, variable target, variable skill,
else sprintf(mstr_skill(502), obj_name(target))); else sprintf(mstr_skill(502), obj_name(target)));
end end
if (user == dude_obj) then begin if (user == dude_obj) then begin
game_time_advance(ONE_GAME_HOUR * healingAttempts); game_time_advance((ONE_GAME_HOUR if (skill == SKILL_DOCTOR) else (30 * ONE_GAME_MINUTE)) * healingAttempts);
end end
end // TODO: not sure if this is needed
default: begin //exec_map_update_scripts
debug_msg(mstr_skill(510)); // skill_use: invalid skill used.
return false;
end
end
if (giveExp and user == dude_obj) then
call show_skill_use_messages(skill, successCount);
return true;
end end
@@ -448,7 +322,7 @@ procedure protinst_use_item_on(variable user, variable target, variable item) be
return 0; return 0;
end end
if (not healing_skill_use_impl(user, target, skill, skillBonus)) then if (not use_healing_skill(user, target, skill, skillBonus)) then
return 0; return 0;
if (random(1, 10) != 1) then if (random(1, 10) != 1) then
@@ -511,7 +385,7 @@ procedure useskill_hook begin
isSuccess; isSuccess;
if (skill == SKILL_FIRST_AID or skill == SKILL_DOCTOR or skill == SKILL_REPAIR) then begin if (skill == SKILL_FIRST_AID or skill == SKILL_DOCTOR or skill == SKILL_REPAIR) then begin
isSuccess := healing_skill_use_impl(user, target, skill, skillBonus); isSuccess := use_healing_skill(user, target, skill, skillBonus);
set_sfall_return(0 if isSuccess else 1); set_sfall_return(0 if isSuccess else 1);
end end
end end
@@ -521,13 +395,5 @@ procedure start begin
if (game_loaded) then begin if (game_loaded) then begin
register_hook_proc(HOOK_USEOBJON, useobjon_hook); register_hook_proc(HOOK_USEOBJON, useobjon_hook);
register_hook_proc(HOOK_USESKILL, useskill_hook); register_hook_proc(HOOK_USESKILL, useskill_hook);
times_skill_used := create_array_list(SKILL_COUNT * SKILLS_MAX_USES_PER_DAY);
healable_damage_flags := array_fixed([
DAM_BLIND,
DAM_CRIP_ARM_LEFT,
DAM_CRIP_ARM_RIGHT,
DAM_CRIP_LEG_RIGHT,
DAM_CRIP_LEG_LEFT
]);
end end
end end