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,270 +137,139 @@ 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); isRobot := (critter_body_type(target) == CR_BODY_ROBOTIC);
minHpToHeal := 4 * healerRank; isRepair := (skill == SKILL_REPAIR);
maxHpToHeal := 10 * healerRank; if (isRepair and not isRobot) then begin
// You cannot repair that.
display_msg(mstr_skill(553));
return false;
end
if (skill_use_slot_available(skill) == -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 end
giveExp := true; isFirstAid := (skill == SKILL_FIRST_AID);
curHp := critter_cur_hp(target); curHp := critter_cur_hp(target);
maxHp := critter_max_hp(target); maxHp := critter_max_hp(target);
critChance := get_critter_stat(user, STAT_crit_chance) + skillBonus;
healingAttempts := 1; 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;
switch (skill) begin fade_out;
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) // Heal crippled limbs.
if (critter_body_type(target) != CR_BODY_ROBOTIC) if (not isFirstAid and (not isRepair or not isRobot) and critter_is_crippled(target)) then begin
else ROLL_FAILURE; // 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
if (not critter_has_dmg_flag(target, healable_damage_flags[i])) then
continue;
healingAttempts += 1;
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
hpToHeal = random(minHpToHeal + 1, maxHpToHeal + 5); clear_critter_dmg_flag(target, healable_damage_flags[i]);
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); clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
call skill_use_slot_add(SKILL_FIRST_AID); // 520: You heal your %s.
// 521: You heal the %s.
// debug skill usage prefixMsg := 520 if (target == dude_obj) else 521;
//display_msg(debug_array_str(array_slice(times_skill_used, skill * SKILLS_MAX_USES_PER_DAY, SKILLS_MAX_USES_PER_DAY))); call skill_use_slot_add(skill);
successCount := 1; skillUseSlotAdded := true;
end else begin end else begin
// You fail to do any healing. // 525: You fail to heal your %s.
display_msg(sprintf(mstr_skill(503), how_much(0))); // 526: You fail to heal the %s.
prefixMsg := 525 if (target == dude_obj) else 526;
end end
// 530: damaged eye
fade_in; // 531: crippled left arm
end else if (user == dude_obj) then begin // 532: crippled right arm
// 501: You look healty already // 533: crippled right leg
// 502: %s looks healthy already // 534: crippled left leg
display_msg(mstr_skill(501) display_msg(sprintf(mstr_skill(prefixMsg), mstr_skill(530 + i)));
if (target == dude_obj) if (user == dude_obj) then
else sprintf(mstr_skill(502), obj_name(target))); call show_skill_use_messages(skill);
end end
end
// Restore hit points.
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
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);
if (user == dude_obj) then begin if (user == dude_obj) then begin
game_time_advance(30 * ONE_GAME_MINUTE); // You heal %d hit points.
display_msg(sprintf(mstr_skill(500), math_min(maxHp - curHp, hpToHeal)));
end end
if (not skillUseSlotAdded) then
call skill_use_slot_add(skill);
clear_critter_combat_flag(target, COMBAT_STATE_FLEEING);
if (user == dude_obj) then
call show_skill_use_messages(skill);
end else begin
// You fail to do any healing.
display_msg(sprintf(mstr_skill(503), how_much(0)));
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 fade_in;
for (i := 0; i < len_array(healable_damage_flags); i++) begin end else if (user == dude_obj) then begin
if ((critter_dmg_flags(target) bwand healable_damage_flags[i]) != 0) then begin // 501: You look healty already
healingAttempts += 1; // 502: %s looks healthy already
roll := roll_vs_skill(user, skill, critChance); display_msg(mstr_skill(501)
if (roll == ROLL_SUCCESS or roll == ROLL_CRITICAL_SUCCESS) then begin if (target == dude_obj)
clear_critter_dmg_flag(target, healable_damage_flags[i]); else sprintf(mstr_skill(502), obj_name(target)));
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.
display_msg(mstr_skill(553));
return false;
end
if (skill_use_slot_available(SKILL_REPAIR) == -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
// You got it?
display_msg(mstr_skill(1101));
return false;
end
if (curHp < maxHp or critter_is_crippled(target)) then begin
fade_out;
// Healing of crippled limbs
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_REPAIR);
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
// 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
roll := roll_check(has_skill(user, skill), critChance);
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_REPAIR);
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
default: begin
debug_msg(mstr_skill(510)); // skill_use: invalid skill used.
return false;
end
end end
if (giveExp and user == dude_obj) then if (user == dude_obj) then begin
call show_skill_use_messages(skill, successCount); game_time_advance((ONE_GAME_HOUR if (skill == SKILL_DOCTOR) else (30 * ONE_GAME_MINUTE)) * healingAttempts);
end
return true; // TODO: not sure if this is needed
//exec_map_update_scripts
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