Pets a lot nicer now, hopefully most of the bugs fixed.
This commit is contained in:
@@ -318,7 +318,7 @@ void run_game()
|
||||
|
||||
// Health Key
|
||||
if (key_pressed(KEY_H)) {
|
||||
speak_with_history(player_health + " health of " + max_health, true);
|
||||
speak_with_history(get_health_report(), true);
|
||||
}
|
||||
|
||||
// Coordinates Key
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
Billy Wolfe: Designer, coder, and sounds
|
||||
https://stormux.org
|
||||
|
||||
Zack Benton: Sounds
|
||||
|
||||
Michael Taboada: sounds
|
||||
Chris Wright: Beta Tester
|
||||
|
||||
Deedra Waters: Beta Tester
|
||||
|
||||
Michael Taboada: sounds
|
||||
|
||||
Sarah Russell: Beta Tester
|
||||
|
||||
Zack Benton: Beta Tester, Mac Support, Sounds
|
||||
|
||||
@@ -143,6 +143,32 @@ HideoutBandit@ get_hideout_bandit_at(int pos) {
|
||||
return null;
|
||||
}
|
||||
|
||||
bool pet_find_hideout_target(int originPos, int referencePos, int &out targetPos, string &out targetLabel, int &out targetKind) {
|
||||
int bestDistance = PET_RANGE + 1;
|
||||
targetPos = -1;
|
||||
targetLabel = "";
|
||||
targetKind = -1;
|
||||
|
||||
for (uint i = 0; i < hideoutBandits.length(); i++) {
|
||||
int distanceToOrigin = abs(hideoutBandits[i].position - originPos);
|
||||
if (distanceToOrigin > PET_RANGE) continue;
|
||||
int distance = abs(hideoutBandits[i].position - referencePos);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
targetPos = hideoutBandits[i].position;
|
||||
targetLabel = "bandit";
|
||||
targetKind = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return targetPos != -1;
|
||||
}
|
||||
|
||||
bool pet_damage_hideout_target(int targetKind, int targetPos, int damage) {
|
||||
if (targetKind != 0) return false;
|
||||
return damage_hideout_bandit_at(targetPos, damage);
|
||||
}
|
||||
|
||||
int clamp_hideout_spawn_start(int startX) {
|
||||
if (startX < 0) return 0;
|
||||
if (startX >= BANDIT_HIDEOUT_MAP_SIZE) return BANDIT_HIDEOUT_MAP_SIZE - 1;
|
||||
@@ -236,6 +262,7 @@ void init_bandit_hideout_adventure() {
|
||||
|
||||
void cleanup_bandit_hideout_adventure() {
|
||||
clear_hideout_bandits();
|
||||
end_pet_adventure();
|
||||
reset_adventure_combat_state();
|
||||
p.destroy_all();
|
||||
}
|
||||
@@ -258,6 +285,8 @@ void run_bandit_hideout_adventure() {
|
||||
intro.insert_last("Bandits will, of course, not take this lying down.");
|
||||
text_reader_lines(intro, "Adventure", true);
|
||||
|
||||
begin_pet_adventure(@pet_find_hideout_target, @pet_damage_hideout_target, hideoutPlayerX);
|
||||
|
||||
bool adventurePaused = false;
|
||||
while (true) {
|
||||
wait(5);
|
||||
@@ -296,7 +325,7 @@ void run_bandit_hideout_adventure() {
|
||||
check_speech_history_keys();
|
||||
|
||||
if (key_pressed(KEY_H)) {
|
||||
speak_with_history(player_health + " health of " + max_health, true);
|
||||
speak_with_history(get_health_report(), true);
|
||||
}
|
||||
|
||||
if (key_pressed(KEY_X)) {
|
||||
@@ -312,6 +341,8 @@ void run_bandit_hideout_adventure() {
|
||||
update_hideout_search();
|
||||
|
||||
update_hideout_bandits();
|
||||
update_pet_adventure_position(hideoutPlayerX);
|
||||
update_pets();
|
||||
adventure_update_bow_shot(hideoutPlayerX);
|
||||
|
||||
if (hideoutBarricadeHealth <= 0) {
|
||||
|
||||
@@ -86,6 +86,7 @@ void init_unicorn_adventure() {
|
||||
void cleanup_unicorn_adventure() {
|
||||
p.destroy_all();
|
||||
reset_adventure_combat_state();
|
||||
end_pet_adventure();
|
||||
if (unicorn.sound_handle != -1) {
|
||||
p.destroy_sound(unicorn.sound_handle);
|
||||
unicorn.sound_handle = -1;
|
||||
@@ -118,6 +119,8 @@ void run_unicorn_adventure() {
|
||||
intro.insert_last("Controls: LEFT/RIGHT to move, UP to jump, CTRL to attack, ESC to flee");
|
||||
text_reader_lines(intro, "Adventure", true);
|
||||
|
||||
begin_pet_adventure(@pet_find_unicorn_target, @pet_damage_unicorn_target, player_arena_x);
|
||||
|
||||
// Adventure Loop
|
||||
bool adventurePaused = false;
|
||||
while (true) {
|
||||
@@ -159,7 +162,7 @@ void run_unicorn_adventure() {
|
||||
|
||||
// Health
|
||||
if (key_pressed(KEY_H)) {
|
||||
speak_with_history(player_health + " health of " + max_health, true);
|
||||
speak_with_history(get_health_report(), true);
|
||||
}
|
||||
|
||||
// Coordinates
|
||||
@@ -177,6 +180,8 @@ void run_unicorn_adventure() {
|
||||
update_unicorn();
|
||||
adventure_update_bow_shot(player_arena_x);
|
||||
update_unicorn_weapon_range_audio();
|
||||
update_pet_adventure_position(player_arena_x);
|
||||
update_pets();
|
||||
|
||||
// Check Conditions - unicorn falls when on collapsed bridge
|
||||
if (!unicorn_defeated && bridge_collapsed && unicorn.x >= BRIDGE_START && unicorn.x <= BRIDGE_END) {
|
||||
@@ -496,6 +501,24 @@ void apply_unicorn_damage(int damage) {
|
||||
}
|
||||
}
|
||||
|
||||
bool pet_find_unicorn_target(int originPos, int referencePos, int &out targetPos, string &out targetLabel, int &out targetKind) {
|
||||
targetPos = -1;
|
||||
targetLabel = "";
|
||||
targetKind = -1;
|
||||
if (unicorn.health <= 0) return false;
|
||||
if (abs(unicorn.x - originPos) > PET_RANGE) return false;
|
||||
targetPos = unicorn.x;
|
||||
targetLabel = "unicorn";
|
||||
targetKind = 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool pet_damage_unicorn_target(int targetKind, int targetPos, int damage) {
|
||||
if (targetKind != 0) return false;
|
||||
apply_unicorn_damage(damage);
|
||||
return true;
|
||||
}
|
||||
|
||||
void play_unicorn_ground_death_sequence() {
|
||||
if (unicorn.sound_handle != -1) {
|
||||
p.destroy_sound(unicorn.sound_handle);
|
||||
|
||||
@@ -266,6 +266,9 @@ const int PET_LOYALTY_BONUS_THRESHOLD = 5;
|
||||
const int PET_MOVE_SPEED = 320; // Slightly faster than base walk speed
|
||||
const int PET_TRAVEL_MIN_MS = 100;
|
||||
const int PET_RANGE = BOW_RANGE + 2;
|
||||
const int PET_HEALTH_MAX = 10;
|
||||
const int PET_ATTACK_SELF_DAMAGE = 1;
|
||||
const int PET_KNOCKOUT_COOLDOWN_HOURS = 3;
|
||||
|
||||
// Goose settings
|
||||
const int GOOSE_HEALTH = 1;
|
||||
|
||||
@@ -13,7 +13,8 @@ string[] learnSoundSkipList = {
|
||||
"sounds/actions/fishpole.ogg",
|
||||
"sounds/actions/hit_ground.ogg",
|
||||
"sounds/menu/",
|
||||
"sounds/nature/"
|
||||
"sounds/nature/",
|
||||
"sounds/pets/"
|
||||
};
|
||||
|
||||
// Description entries: keep paths/texts aligned by index.
|
||||
|
||||
@@ -39,7 +39,13 @@ void run_character_info_menu() {
|
||||
options.insert_last("Favor " + format_favor(favor) + ".");
|
||||
options.insert_last("Speed " + get_speed_status() + ".");
|
||||
if (petActive) {
|
||||
options.insert_last("Pet " + petType + ". Gender " + petGender + ". Loyalty " + petLoyalty + ".");
|
||||
string petInfo = "Pet " + get_pet_display_name() + ". " + petHealth + " health of " + PET_HEALTH_MAX + ".";
|
||||
petInfo += " Loyalty " + petLoyalty + " of " + PET_LOYALTY_MAX + ".";
|
||||
if (petKnockoutHoursRemaining > 0) {
|
||||
string hourLabel = (petKnockoutHoursRemaining == 1) ? "hour" : "hours";
|
||||
petInfo += " Knocked out. " + petKnockoutHoursRemaining + " " + hourLabel + " remaining.";
|
||||
}
|
||||
options.insert_last(petInfo);
|
||||
} else {
|
||||
options.insert_last("Pet none.");
|
||||
}
|
||||
|
||||
@@ -9,6 +9,8 @@ int petLoyalty = 0;
|
||||
bool petOut = false;
|
||||
int petPosition = 0;
|
||||
bool petPositionValid = false;
|
||||
int petHealth = 0;
|
||||
int petKnockoutHoursRemaining = 0;
|
||||
|
||||
timer petAttackTimer;
|
||||
timer petRetrieveTimer;
|
||||
@@ -35,6 +37,17 @@ timer petTravelTimer;
|
||||
string[] petSoundPaths;
|
||||
bool petSoundsInitialized = false;
|
||||
|
||||
funcdef bool PetAdventureFindTargetCallback(int originPos, int referencePos, int &out targetPos, string &out targetLabel, int &out targetKind);
|
||||
funcdef bool PetAdventureDamageCallback(int targetKind, int targetPos, int damage);
|
||||
|
||||
bool petAdventureMode = false;
|
||||
int petAdventurePlayerPos = -1;
|
||||
bool petAdventurePreOut = false;
|
||||
int petAdventurePrePosition = 0;
|
||||
bool petAdventurePrePositionValid = false;
|
||||
PetAdventureFindTargetCallback@ petAdventureFindTarget = null;
|
||||
PetAdventureDamageCallback@ petAdventureDamageTarget = null;
|
||||
|
||||
string normalize_pet_path(const string&in path) {
|
||||
return path.replace("\\", "/", true);
|
||||
}
|
||||
@@ -47,6 +60,15 @@ string collapse_pet_spaces(const string&in text) {
|
||||
return result;
|
||||
}
|
||||
|
||||
string normalize_pet_name_text(const string&in raw) {
|
||||
string name = raw;
|
||||
name = name.replace("_", " ", true);
|
||||
name = name.replace("-", " ", true);
|
||||
name = collapse_pet_spaces(name);
|
||||
name.trim_whitespace_this();
|
||||
return name;
|
||||
}
|
||||
|
||||
void gather_pet_sound_files(const string&in basePath, string[]@ outFiles) {
|
||||
string[]@ files = find_files(basePath + "/*.ogg");
|
||||
if (@files !is null) {
|
||||
@@ -179,6 +201,89 @@ void queue_pet_event(const string&in message, int soundPos = -1) {
|
||||
petEventPositions.insert_last(soundPos);
|
||||
}
|
||||
|
||||
string get_pet_display_name() {
|
||||
string name = normalize_pet_name_text(petType);
|
||||
if (name == "") return "pet";
|
||||
return name;
|
||||
}
|
||||
|
||||
int get_pet_listener_pos() {
|
||||
if (petAdventurePlayerPos >= 0) return petAdventurePlayerPos;
|
||||
return x;
|
||||
}
|
||||
|
||||
string get_health_report() {
|
||||
string report = player_health + " health of " + max_health;
|
||||
if (petActive) {
|
||||
report += ", " + get_pet_display_name() + ", " + petHealth + " health of " + PET_HEALTH_MAX;
|
||||
report += ", loyalty " + petLoyalty + " of " + PET_LOYALTY_MAX;
|
||||
}
|
||||
return report;
|
||||
}
|
||||
|
||||
int get_pet_search_origin() {
|
||||
if (petOut && petPositionValid) return petPosition;
|
||||
return get_pet_listener_pos();
|
||||
}
|
||||
|
||||
bool is_pet_knocked_out() {
|
||||
return petKnockoutHoursRemaining > 0 || petHealth <= 0;
|
||||
}
|
||||
|
||||
void begin_pet_adventure(PetAdventureFindTargetCallback@ findTarget, PetAdventureDamageCallback@ damageTarget, int playerPos) {
|
||||
petAdventureMode = true;
|
||||
petAdventurePlayerPos = playerPos;
|
||||
petAdventurePreOut = petOut;
|
||||
petAdventurePrePosition = petPosition;
|
||||
petAdventurePrePositionValid = petPositionValid;
|
||||
@petAdventureFindTarget = findTarget;
|
||||
@petAdventureDamageTarget = damageTarget;
|
||||
stop_pet_travel();
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
}
|
||||
|
||||
void update_pet_adventure_position(int playerPos) {
|
||||
petAdventurePlayerPos = playerPos;
|
||||
}
|
||||
|
||||
void end_pet_adventure() {
|
||||
petAdventureMode = false;
|
||||
petAdventurePlayerPos = -1;
|
||||
@petAdventureFindTarget = null;
|
||||
@petAdventureDamageTarget = null;
|
||||
stop_pet_travel();
|
||||
if (is_pet_knocked_out()) {
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
} else if (petAdventurePreOut) {
|
||||
petOut = true;
|
||||
petPosition = petAdventurePrePosition;
|
||||
petPositionValid = petAdventurePrePositionValid;
|
||||
} else {
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
}
|
||||
petAdventurePreOut = false;
|
||||
petAdventurePrePosition = 0;
|
||||
petAdventurePrePositionValid = false;
|
||||
}
|
||||
|
||||
void queue_pet_return_event() {
|
||||
if (!petActive) return;
|
||||
queue_pet_event("A " + get_pet_display_name() + " returns to you.");
|
||||
}
|
||||
|
||||
void knock_out_pet() {
|
||||
if (!petActive) return;
|
||||
petHealth = 0;
|
||||
petKnockoutHoursRemaining = PET_KNOCKOUT_COOLDOWN_HOURS;
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
stop_pet_travel();
|
||||
queue_pet_event("Your " + get_pet_display_name() + " has been knocked out.");
|
||||
}
|
||||
|
||||
void update_pet_events() {
|
||||
if (petEventMessages.length() == 0) {
|
||||
petEventSoundHandle = -1;
|
||||
@@ -202,7 +307,7 @@ void update_pet_events() {
|
||||
int soundPos = petEventPositions[0];
|
||||
if (soundPath != "" && file_exists(soundPath)) {
|
||||
if (soundPos >= 0) {
|
||||
petEventSoundHandle = play_1d_with_volume_step(soundPath, x, soundPos, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
||||
petEventSoundHandle = play_1d_with_volume_step(soundPath, get_pet_listener_pos(), soundPos, false, PLAYER_WEAPON_SOUND_VOLUME_STEP);
|
||||
} else {
|
||||
petEventSoundHandle = p.play_stationary(soundPath, false);
|
||||
}
|
||||
@@ -227,9 +332,14 @@ void reset_pet_state() {
|
||||
petOut = false;
|
||||
petPosition = 0;
|
||||
petPositionValid = false;
|
||||
petHealth = PET_HEALTH_MAX;
|
||||
petKnockoutHoursRemaining = 0;
|
||||
petHealth = 0;
|
||||
petKnockoutHoursRemaining = 0;
|
||||
petEventMessages.resize(0);
|
||||
petEventSounds.resize(0);
|
||||
petEventPositions.resize(0);
|
||||
safe_destroy_sound(petEventSoundHandle);
|
||||
petEventSoundHandle = -1;
|
||||
petTravelActive = false;
|
||||
petTravelAction = PET_TRAVEL_NONE;
|
||||
@@ -246,7 +356,7 @@ void reset_pet_state() {
|
||||
}
|
||||
|
||||
void pet_leave() {
|
||||
string oldPet = petType;
|
||||
string oldPet = normalize_pet_name_text(petType);
|
||||
reset_pet_state();
|
||||
if (oldPet != "") {
|
||||
speak_with_history(oldPet + " leaves.", true);
|
||||
@@ -273,17 +383,26 @@ void check_pet_call_key() {
|
||||
speak_with_history("No pet.", true);
|
||||
return;
|
||||
}
|
||||
if (is_pet_knocked_out()) {
|
||||
string message = "Your " + get_pet_display_name() + " has been knocked out.";
|
||||
if (petKnockoutHoursRemaining > 0) {
|
||||
string hourLabel = (petKnockoutHoursRemaining == 1) ? "hour" : "hours";
|
||||
message += " " + petKnockoutHoursRemaining + " " + hourLabel + " remaining.";
|
||||
}
|
||||
speak_with_history(message, true);
|
||||
return;
|
||||
}
|
||||
if (petOut) {
|
||||
petOut = false;
|
||||
stop_pet_travel();
|
||||
petPositionValid = false;
|
||||
speak_with_history("Pet called back.", true);
|
||||
queue_pet_return_event();
|
||||
return;
|
||||
}
|
||||
adjust_pet_loyalty(-PET_LOYALTY_CALLOUT_COST);
|
||||
if (!petActive) return;
|
||||
petOut = true;
|
||||
petPosition = x;
|
||||
petPosition = get_pet_listener_pos();
|
||||
petPositionValid = true;
|
||||
if (file_exists("sounds/pets/call_pet.ogg")) {
|
||||
p.play_stationary("sounds/pets/call_pet.ogg", false);
|
||||
@@ -299,10 +418,12 @@ void adopt_pet(const string&in soundPath) {
|
||||
petOut = false;
|
||||
petPosition = 0;
|
||||
petPositionValid = false;
|
||||
petHealth = PET_HEALTH_MAX;
|
||||
petKnockoutHoursRemaining = 0;
|
||||
petAttackTimer.restart();
|
||||
petRetrieveTimer.restart();
|
||||
petTravelTimer.restart();
|
||||
speak_with_history("A " + petType + " joins you.", true);
|
||||
speak_with_history("A " + get_pet_display_name() + " joins you.", true);
|
||||
}
|
||||
|
||||
void stop_pet_travel() {
|
||||
@@ -328,7 +449,7 @@ void start_pet_travel_attack(int targetPos, const string&in targetLabel, int tar
|
||||
stop_pet_travel();
|
||||
petTravelActive = true;
|
||||
petTravelAction = PET_TRAVEL_ATTACK;
|
||||
petTravelStartPos = petPositionValid ? petPosition : x;
|
||||
petTravelStartPos = petPositionValid ? petPosition : get_pet_listener_pos();
|
||||
petTravelTargetPos = targetPos;
|
||||
petTravelTargetLabel = targetLabel;
|
||||
petTravelTargetKind = targetKind;
|
||||
@@ -338,7 +459,7 @@ void start_pet_travel_attack(int targetPos, const string&in targetLabel, int tar
|
||||
if (petSoundPath != "" && file_exists(petSoundPath)) {
|
||||
petTravelSoundHandle = play_1d_with_volume_step(
|
||||
petSoundPath,
|
||||
x,
|
||||
get_pet_listener_pos(),
|
||||
petTravelStartPos,
|
||||
true,
|
||||
PLAYER_WEAPON_SOUND_VOLUME_STEP
|
||||
@@ -350,7 +471,7 @@ void start_pet_travel_retrieve(int targetPos) {
|
||||
stop_pet_travel();
|
||||
petTravelActive = true;
|
||||
petTravelAction = PET_TRAVEL_RETRIEVE;
|
||||
petTravelStartPos = petPositionValid ? petPosition : x;
|
||||
petTravelStartPos = petPositionValid ? petPosition : get_pet_listener_pos();
|
||||
petTravelTargetPos = targetPos;
|
||||
petTravelDurationMs = get_pet_travel_duration_ms(petTravelStartPos, targetPos);
|
||||
petTravelTimer.restart();
|
||||
@@ -360,6 +481,13 @@ void update_pet_travel() {
|
||||
if (!petTravelActive) return;
|
||||
if (petTravelDurationMs < 1) petTravelDurationMs = 1;
|
||||
|
||||
if (petTravelAction == PET_TRAVEL_ATTACK) {
|
||||
int refreshedTargetPos = -1;
|
||||
if (find_pet_attack_target_by_kind(petTravelTargetKind, petTravelTargetPos, refreshedTargetPos)) {
|
||||
petTravelTargetPos = refreshedTargetPos;
|
||||
}
|
||||
}
|
||||
|
||||
int elapsed = petTravelTimer.elapsed;
|
||||
float progress = float(elapsed) / float(petTravelDurationMs);
|
||||
if (progress > 1.0f) progress = 1.0f;
|
||||
@@ -378,24 +506,40 @@ void update_pet_travel() {
|
||||
if (petTravelAction == PET_TRAVEL_ATTACK) {
|
||||
int damage = BOW_DAMAGE_MAX;
|
||||
bool hit = false;
|
||||
if (petTravelTargetKind == 0) {
|
||||
hit = damage_bandit_at(petTravelTargetPos, damage);
|
||||
} else if (petTravelTargetKind == 1) {
|
||||
hit = damage_undead_at(petTravelTargetPos, damage);
|
||||
} else if (petTravelTargetKind == 2) {
|
||||
hit = damage_boar_at(petTravelTargetPos, damage);
|
||||
if (petAdventureMode && @petAdventureDamageTarget !is null) {
|
||||
hit = petAdventureDamageTarget(petTravelTargetKind, petTravelTargetPos, damage);
|
||||
} else {
|
||||
if (petTravelTargetKind == 0) {
|
||||
hit = damage_bandit_at(petTravelTargetPos, damage);
|
||||
} else if (petTravelTargetKind == 1) {
|
||||
hit = damage_undead_at(petTravelTargetPos, damage);
|
||||
} else if (petTravelTargetKind == 2) {
|
||||
hit = damage_boar_at(petTravelTargetPos, damage);
|
||||
}
|
||||
}
|
||||
if (hit) {
|
||||
queue_pet_event("Your " + petType + " attacks the " + petTravelTargetLabel + ".", petTravelTargetPos);
|
||||
petHealth -= PET_ATTACK_SELF_DAMAGE;
|
||||
if (petHealth <= 0) {
|
||||
knock_out_pet();
|
||||
return;
|
||||
}
|
||||
}
|
||||
petPosition = petTravelTargetPos;
|
||||
petPositionValid = true;
|
||||
int nextTargetPos = -1;
|
||||
string nextTargetLabel = "";
|
||||
int nextTargetKind = -1;
|
||||
if (!find_pet_attack_target(nextTargetPos, nextTargetLabel, nextTargetKind)) {
|
||||
bool hasNextTarget = find_pet_attack_target(nextTargetPos, nextTargetLabel, nextTargetKind);
|
||||
if (!hasNextTarget) {
|
||||
WorldDrop@ drop = find_pet_drop_target();
|
||||
if (drop !is null) {
|
||||
petRetrieveTimer.restart();
|
||||
start_pet_travel_retrieve(drop.position);
|
||||
return;
|
||||
}
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
queue_pet_return_event();
|
||||
}
|
||||
} else if (petTravelAction == PET_TRAVEL_RETRIEVE) {
|
||||
WorldDrop@ drop = get_drop_at(petTravelTargetPos);
|
||||
@@ -404,8 +548,25 @@ void update_pet_travel() {
|
||||
if (try_pet_pickup_world_drop(drop, message)) {
|
||||
remove_drop_at(drop.position);
|
||||
queue_pet_event(message);
|
||||
petPosition = petTravelTargetPos;
|
||||
petPositionValid = true;
|
||||
WorldDrop@ nextDrop = find_pet_drop_target();
|
||||
if (nextDrop !is null) {
|
||||
petRetrieveTimer.restart();
|
||||
start_pet_travel_retrieve(nextDrop.position);
|
||||
return;
|
||||
}
|
||||
int nextTargetPos = -1;
|
||||
string nextTargetLabel = "";
|
||||
int nextTargetKind = -1;
|
||||
if (find_pet_attack_target(nextTargetPos, nextTargetLabel, nextTargetKind)) {
|
||||
petAttackTimer.restart();
|
||||
start_pet_travel_attack(nextTargetPos, nextTargetLabel, nextTargetKind);
|
||||
return;
|
||||
}
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
queue_pet_return_event();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -495,11 +656,12 @@ void attempt_pet_offer_from_tree() {
|
||||
|
||||
bool try_pet_pickup_small_game(const string&in gameType, string &out message) {
|
||||
if (get_personal_count(ITEM_SMALL_GAME) >= get_personal_stack_limit()) {
|
||||
return false;
|
||||
message = "You can't carry a " + gameType + ", so you give it to a " + get_pet_display_name() + " to chew on.";
|
||||
return true;
|
||||
}
|
||||
add_personal_count(ITEM_SMALL_GAME, 1);
|
||||
personal_small_game_types.insert_last(gameType);
|
||||
message = "Your " + petType + " retrieved " + gameType + ".";
|
||||
message = "Your " + get_pet_display_name() + " retrieved " + gameType + ". A " + get_pet_display_name() + " returns to you.";
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -511,21 +673,24 @@ bool try_pet_pickup_world_drop(WorldDrop@ drop, string &out message) {
|
||||
if (drop.type == "arrow") {
|
||||
int maxArrows = get_arrow_limit();
|
||||
if (maxArrows <= 0) {
|
||||
return false;
|
||||
message = "You can't carry an arrow, so you give it to a " + get_pet_display_name() + " to chew on.";
|
||||
return true;
|
||||
}
|
||||
if (get_personal_count(ITEM_ARROWS) >= maxArrows) {
|
||||
return false;
|
||||
message = "You can't carry an arrow, so you give it to a " + get_pet_display_name() + " to chew on.";
|
||||
return true;
|
||||
}
|
||||
add_personal_count(ITEM_ARROWS, 1);
|
||||
message = "Your " + petType + " retrieved an arrow.";
|
||||
message = "Your " + get_pet_display_name() + " retrieved an arrow. A " + get_pet_display_name() + " returns to you.";
|
||||
return true;
|
||||
}
|
||||
if (drop.type == "boar carcass") {
|
||||
if (get_personal_count(ITEM_BOAR_CARCASSES) >= get_personal_stack_limit()) {
|
||||
return false;
|
||||
message = "You can't carry a boar carcass, so you give it to a " + get_pet_display_name() + " to chew on.";
|
||||
return true;
|
||||
}
|
||||
add_personal_count(ITEM_BOAR_CARCASSES, 1);
|
||||
message = "Your " + petType + " retrieved a boar carcass.";
|
||||
message = "Your " + get_pet_display_name() + " retrieved a boar carcass. A " + get_pet_display_name() + " returns to you.";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -534,8 +699,9 @@ bool try_pet_pickup_world_drop(WorldDrop@ drop, string &out message) {
|
||||
WorldDrop@ find_pet_drop_target() {
|
||||
int bestDistance = PET_RANGE + 1;
|
||||
WorldDrop@ best = null;
|
||||
int origin = get_pet_search_origin();
|
||||
for (uint i = 0; i < world_drops.length(); i++) {
|
||||
int distance = abs(world_drops[i].position - x);
|
||||
int distance = abs(world_drops[i].position - origin);
|
||||
if (distance > PET_RANGE) continue;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
@@ -549,6 +715,8 @@ void update_pet_retrieval() {
|
||||
if (!petActive) return;
|
||||
if (!petOut) return;
|
||||
if (petLoyalty <= 0) return;
|
||||
if (petAdventureMode) return;
|
||||
if (is_pet_knocked_out()) return;
|
||||
if (petRetrieveTimer.elapsed < PET_RETRIEVE_COOLDOWN) return;
|
||||
if (petEventMessages.length() > 2) return;
|
||||
if (petTravelActive) return;
|
||||
@@ -565,9 +733,13 @@ bool find_pet_attack_target(int &out targetPos, string &out targetLabel, int &ou
|
||||
targetPos = -1;
|
||||
targetLabel = "";
|
||||
targetKind = -1;
|
||||
int origin = get_pet_search_origin();
|
||||
if (petAdventureMode && @petAdventureFindTarget !is null) {
|
||||
return petAdventureFindTarget(origin, origin, targetPos, targetLabel, targetKind);
|
||||
}
|
||||
|
||||
for (uint i = 0; i < bandits.length(); i++) {
|
||||
int distance = abs(bandits[i].position - x);
|
||||
int distance = abs(bandits[i].position - origin);
|
||||
if (distance > PET_RANGE) continue;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
@@ -579,7 +751,7 @@ bool find_pet_attack_target(int &out targetPos, string &out targetLabel, int &ou
|
||||
|
||||
for (uint i = 0; i < undeads.length(); i++) {
|
||||
if (undeads[i].undead_type == "undead_resident") continue;
|
||||
int distance = abs(undeads[i].position - x);
|
||||
int distance = abs(undeads[i].position - origin);
|
||||
if (distance > PET_RANGE) continue;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
@@ -590,7 +762,7 @@ bool find_pet_attack_target(int &out targetPos, string &out targetLabel, int &ou
|
||||
}
|
||||
|
||||
for (uint i = 0; i < ground_games.length(); i++) {
|
||||
int distance = abs(ground_games[i].position - x);
|
||||
int distance = abs(ground_games[i].position - origin);
|
||||
if (distance > PET_RANGE) continue;
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
@@ -603,10 +775,57 @@ bool find_pet_attack_target(int &out targetPos, string &out targetLabel, int &ou
|
||||
return targetPos != -1;
|
||||
}
|
||||
|
||||
bool find_pet_attack_target_by_kind(int targetKind, int referencePos, int &out targetPos) {
|
||||
int bestDistance = PET_RANGE + 1;
|
||||
targetPos = -1;
|
||||
int origin = get_pet_search_origin();
|
||||
if (petAdventureMode && @petAdventureFindTarget !is null) {
|
||||
string targetLabel = "";
|
||||
int foundKind = -1;
|
||||
return petAdventureFindTarget(origin, referencePos, targetPos, targetLabel, foundKind);
|
||||
}
|
||||
|
||||
if (targetKind == 0) {
|
||||
for (uint i = 0; i < bandits.length(); i++) {
|
||||
int distanceToOrigin = abs(bandits[i].position - origin);
|
||||
if (distanceToOrigin > PET_RANGE) continue;
|
||||
int distance = abs(bandits[i].position - referencePos);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
targetPos = bandits[i].position;
|
||||
}
|
||||
}
|
||||
} else if (targetKind == 1) {
|
||||
for (uint i = 0; i < undeads.length(); i++) {
|
||||
if (undeads[i].undead_type == "undead_resident") continue;
|
||||
int distanceToOrigin = abs(undeads[i].position - origin);
|
||||
if (distanceToOrigin > PET_RANGE) continue;
|
||||
int distance = abs(undeads[i].position - referencePos);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
targetPos = undeads[i].position;
|
||||
}
|
||||
}
|
||||
} else if (targetKind == 2) {
|
||||
for (uint i = 0; i < ground_games.length(); i++) {
|
||||
int distanceToOrigin = abs(ground_games[i].position - origin);
|
||||
if (distanceToOrigin > PET_RANGE) continue;
|
||||
int distance = abs(ground_games[i].position - referencePos);
|
||||
if (distance < bestDistance) {
|
||||
bestDistance = distance;
|
||||
targetPos = ground_games[i].position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return targetPos != -1;
|
||||
}
|
||||
|
||||
void update_pet_attack() {
|
||||
if (!petActive) return;
|
||||
if (!petOut) return;
|
||||
if (petLoyalty <= 0) return;
|
||||
if (is_pet_knocked_out()) return;
|
||||
if (petAttackTimer.elapsed < PET_ATTACK_COOLDOWN) return;
|
||||
if (petTravelActive) return;
|
||||
|
||||
@@ -632,7 +851,7 @@ void attempt_pet_random_find() {
|
||||
|
||||
add_personal_count(itemType, added);
|
||||
string itemName = (added == 1) ? item_registry[itemType].singular : item_registry[itemType].name;
|
||||
queue_pet_event("Your " + petType + " retrieved " + added + " " + itemName + ".", x);
|
||||
queue_pet_event("Your " + get_pet_display_name() + " retrieved " + added + " " + itemName + ". A " + get_pet_display_name() + " returns to you.", x);
|
||||
petOut = false;
|
||||
petPositionValid = false;
|
||||
}
|
||||
@@ -640,7 +859,16 @@ void attempt_pet_random_find() {
|
||||
void handle_pet_hourly_update(int hour) {
|
||||
if (!petActive) return;
|
||||
|
||||
if (get_pet_food_personal_total() == 0) {
|
||||
if (petKnockoutHoursRemaining > 0) {
|
||||
petKnockoutHoursRemaining--;
|
||||
if (petKnockoutHoursRemaining <= 0) {
|
||||
petKnockoutHoursRemaining = 0;
|
||||
petHealth = PET_HEALTH_MAX;
|
||||
notify("Your " + get_pet_display_name() + " has recovered from its injuries.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_pet_food_available()) {
|
||||
adjust_pet_loyalty(-PET_LOYALTY_HUNGER_LOSS);
|
||||
}
|
||||
|
||||
|
||||
@@ -806,6 +806,8 @@ bool save_game_state() {
|
||||
saveData.set("pet_type", petType);
|
||||
saveData.set("pet_gender", petGender);
|
||||
saveData.set("pet_loyalty", petLoyalty);
|
||||
saveData.set("pet_health", petHealth);
|
||||
saveData.set("pet_ko_hours_remaining", petKnockoutHoursRemaining);
|
||||
|
||||
// Save inventory arrays using new compact format
|
||||
saveData.set("personal_inventory", serialize_inventory_array(personal_inventory));
|
||||
@@ -1129,11 +1131,15 @@ bool load_game_state_from_file(const string&in filename) {
|
||||
petGender = loadedPetGender;
|
||||
}
|
||||
petLoyalty = int(get_number(saveData, "pet_loyalty", 0));
|
||||
petHealth = int(get_number(saveData, "pet_health", PET_HEALTH_MAX));
|
||||
petKnockoutHoursRemaining = int(get_number(saveData, "pet_ko_hours_remaining", 0));
|
||||
if (!petActive) {
|
||||
petSoundPath = "";
|
||||
petType = "";
|
||||
petGender = "";
|
||||
petLoyalty = 0;
|
||||
petHealth = 0;
|
||||
petKnockoutHoursRemaining = 0;
|
||||
}
|
||||
if (petActive && petSoundPath != "" && !file_exists(petSoundPath)) {
|
||||
petActive = false;
|
||||
@@ -1141,8 +1147,20 @@ bool load_game_state_from_file(const string&in filename) {
|
||||
petType = "";
|
||||
petGender = "";
|
||||
petLoyalty = 0;
|
||||
petHealth = 0;
|
||||
petKnockoutHoursRemaining = 0;
|
||||
}
|
||||
if (petActive) {
|
||||
if (petKnockoutHoursRemaining < 0) petKnockoutHoursRemaining = 0;
|
||||
if (petKnockoutHoursRemaining > PET_KNOCKOUT_COOLDOWN_HOURS) {
|
||||
petKnockoutHoursRemaining = PET_KNOCKOUT_COOLDOWN_HOURS;
|
||||
}
|
||||
if (petKnockoutHoursRemaining > 0) {
|
||||
petHealth = 0;
|
||||
} else {
|
||||
if (petHealth <= 0) petHealth = PET_HEALTH_MAX;
|
||||
if (petHealth > PET_HEALTH_MAX) petHealth = PET_HEALTH_MAX;
|
||||
}
|
||||
petAttackTimer.restart();
|
||||
petRetrieveTimer.restart();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user