TbBool attempt_job_secondary_preference(struct Thing *creatng, long jobpref) { long i; unsigned long k; // Count the amount of jobs set i = 0; k = jobpref; while (k) { k >>= 1; i++; } if (i <= 0) { return false; } unsigned long select_val,select_curr,select_delta; select_val = ACTION_RANDOM(512); select_delta = 512 / i; select_curr = select_delta; // For some reason, this is a bit different than attempt_job_preference(). // Probably needs unification for (i=1; i < crtr_conf.jobs_count; i++) { CreatureJob new_job = 1<<(i-1); if ((jobpref & new_job) == 0) { continue; } SYNCDBG(19,"Check job %s",creature_job_code_name(new_job)); if (select_val <= select_curr) { select_curr += select_delta; } else if (creature_can_do_job_for_player(creatng, creatng->owner, new_job, JobChk_None)) { if (send_creature_to_job_for_player(creatng, creatng->owner, new_job)) { return true; } } } // If no job, give 1% chance of going to temple if (ACTION_RANDOM(100) == 0) { CreatureJob new_job = Job_TEMPLE_PRAY; if (creature_can_do_job_for_player(creatng, creatng->owner, new_job, JobChk_None)) { if (send_creature_to_job_for_player(creatng, creatng->owner, new_job)) { return true; } } } return 0; }
/** * Tries to assign one of creature's preferred jobs to it. * Starts at random job, to make sure all the jobs have equal chance of being selected. * @param creatng The creature to assign a job to. * @param jobpref Job preference flags. */ TbBool attempt_job_preference(struct Thing *creatng, long jobpref) { //return _DK_attempt_job_preference(creatng, jobpref); long i,n; // Start checking at random job if (crtr_conf.jobs_count < 1) { return false; } n = ACTION_RANDOM(crtr_conf.jobs_count); for (i=0; i < crtr_conf.jobs_count; i++, n = (n+1)%crtr_conf.jobs_count) { if (n == 0) continue; CreatureJob new_job; new_job = 1 << (n-1); if ((jobpref & new_job) != 0) { SYNCDBG(19,"Check job %s",creature_job_code_name(new_job)); if (creature_can_do_job_for_player(creatng, creatng->owner, new_job, JobChk_None)) { if (send_creature_to_job_for_player(creatng, creatng->owner, new_job)) { return true; } } } } return false; }
long computer_check_for_pretty(struct Computer2 *comp, struct ComputerCheck * check) { struct Dungeon *dungeon; SYNCDBG(8,"Starting"); dungeon = comp->dungeon; MapSubtlCoord stl_x, stl_y; if (computer_able_to_use_magic(comp, PwrK_HAND, 1, 1) != CTaskRet_Unk1) { return CTaskRet_Unk4; } { long stack_len; stack_len = dungeon->digger_stack_length; if (stack_len <= check->param1 * dungeon->total_area / 100) { return CTaskRet_Unk4; } long n; n = find_in_imp_stack_starting_at(DigTsk_ImproveDungeon, ACTION_RANDOM(stack_len), dungeon); if (n < 0) { return CTaskRet_Unk4; } const struct DiggerStack *dstack; dstack = &dungeon->digger_stack[n]; stl_x = stl_num_decode_x(dstack->stl_num); stl_y = stl_num_decode_y(dstack->stl_num); } struct Thing * creatng; creatng = find_imp_for_pickup(comp, stl_x, stl_y); if (thing_is_invalid(creatng)) { return CTaskRet_Unk4; } if (!create_task_move_creature_to_subtile(comp, creatng, stl_x, stl_y, CrSt_ImpImprovesDungeon)) { return CTaskRet_Unk4; } return CTaskRet_Unk1; }
TbBool attempt_anger_job_join_enemy(struct Thing *creatng) { struct Thing *heartng; int i, n; n = ACTION_RANDOM(PLAYERS_COUNT); for (i=0; i < PLAYERS_COUNT; i++, n=(n+1)%PLAYERS_COUNT) { if ((n == game.neutral_player_num) || (n == creatng->owner)) continue; struct PlayerInfo *player; player = get_player(n); if (!player_exists(player) || (player->field_2C != 1)) continue; heartng = get_player_soul_container(n); if (thing_exists(heartng) && (heartng->active_state != 3)) { TRACE_THING(heartng); if (creature_can_navigate_to(creatng, &heartng->mappos, NavRtF_Default)) { change_creature_owner(creatng, n); anger_set_creature_anger_all_types(creatng, 0); } } } return false; }
long computer_check_for_accelerate(struct Computer2 *comp, struct ComputerCheck * check) { static RoomKind workers_in_rooms[] = {RoK_LIBRARY,RoK_LIBRARY,RoK_WORKSHOP,RoK_TRAINING,RoK_SCAVENGER}; struct Thing *thing; long i,n; SYNCDBG(8,"Starting"); //return _DK_computer_check_for_accelerate(comp, check); if (computer_able_to_use_magic(comp, PwrK_SPEEDCRTR, 8, 3) != 1) { return 4; } n = check->param1 % (sizeof(workers_in_rooms)/sizeof(workers_in_rooms[0])); if (n <= 0) n = ACTION_RANDOM(sizeof(workers_in_rooms)/sizeof(workers_in_rooms[0])); for (i=0; i < sizeof(workers_in_rooms)/sizeof(workers_in_rooms[0]); i++) { thing = computer_check_creatures_in_dungeon_rooms_of_kind_for_accelerate(comp, workers_in_rooms[n]); if (!thing_is_invalid(thing)) { SYNCDBG(8,"Cast on thing %d",(int)thing->index); return 1; } n = (n+1) % (sizeof(workers_in_rooms)/sizeof(workers_in_rooms[0])); } return 4; }
long computer_check_for_expand_room(struct Computer2 *comp, struct ComputerCheck * check) { SYNCDBG(8,"Starting"); //return _DK_computer_check_for_expand_room(comp, check); struct Dungeon *dungeon; dungeon = comp->dungeon; if (dungeon_invalid(dungeon)) { ERRORLOG("Invalid computer players dungeon"); return 0; } long around_start; around_start = ACTION_RANDOM(119); // Don't work when placing rooms; we could place in an area for room by mistake if (is_task_in_progress(comp, CTT_PlaceRoom) || is_task_in_progress(comp, CTT_CheckRoomDug)) { SYNCDBG(8,"No rooms expansion - colliding task already in progress"); return 0; } if (4 * dungeon->creatures_total_pay / 3 >= dungeon->total_money_owned) { SYNCDBG(8,"No rooms expansion - we don't even have money for payday"); return 0; } const struct ExpandRooms *expndroom; for (expndroom = &expand_rooms[0]; expndroom->rkind != RoK_NONE; expndroom++) { if (computer_check_for_expand_room_kind(comp, check, expndroom->rkind, expndroom->max_slabs, around_start)) { return 1; } } SYNCDBG(8,"No rooms found for expansion"); return 0; }
void set_thing_draw(struct Thing *thing, long anim, long speed, long scale, char a5, char start_frame, unsigned char draw_class) { unsigned long i; thing->anim_sprite = convert_td_iso(anim); thing->field_50 &= 0x03; thing->field_50 |= (draw_class << 2); thing->field_49 = keepersprite_frames(thing->anim_sprite); if (speed != -1) { thing->field_3E = speed; } if (scale != -1) { thing->sprite_size = scale; } if (a5 != -1) { set_flag_byte(&thing->field_4F, TF4F_Unknown40, a5); } if (start_frame == -2) { i = keepersprite_frames(thing->anim_sprite) - 1; thing->field_48 = i; thing->field_40 = i << 8; } else if (start_frame == -1) { i = ACTION_RANDOM(thing->field_49); thing->field_48 = i; thing->field_40 = i << 8; } else { i = start_frame; thing->field_48 = i; thing->field_40 = i << 8; } }
void process_disease(struct Thing *creatng) { SYNCDBG(18,"Starting"); //_DK_process_disease(thing); struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(creatng); if (!creature_affected_by_spell(creatng, SplK_Disease)) { return; } if (ACTION_RANDOM(100) < game.disease_transfer_percentage) { SubtlCodedCoords stl_num; long n; stl_num = get_subtile_number(creatng->mappos.x.stl.num,creatng->mappos.y.stl.num); for (n=0; n < AROUND_MAP_LENGTH; n++) { struct Thing *thing; struct Map *mapblk; unsigned long k; long i; mapblk = get_map_block_at_pos(stl_num+around_map[n]); k = 0; i = get_mapwho_thing_index(mapblk); while (i != 0) { thing = thing_get(i); if (thing_is_invalid(thing)) { WARNLOG("Jump out of things array"); break; } i = thing->next_on_mapblk; // Per thing code if (thing_is_creature(thing) && ((get_creature_model_flags(thing) & CMF_IsSpecDigger) == 0) && (thing->owner != cctrl->disease_caster_plyridx) && !creature_affected_by_spell(thing, SplK_Disease)) { struct CreatureControl *tngcctrl; tngcctrl = creature_control_get_from_thing(thing); apply_spell_effect_to_thing(thing, SplK_Disease, cctrl->explevel); tngcctrl->disease_caster_plyridx = cctrl->disease_caster_plyridx; } // Per thing code ends k++; if (k > THINGS_COUNT) { ERRORLOG("Infinite loop detected when sweeping things list"); erstat_inc(ESE_InfChainTngPerMapWho); break_mapwho_infinite_chain(mapblk); break; } } } } if (((game.play_gameturn - cctrl->disease_start_turn) % game.disease_lose_health_time) == 0) { apply_damage_to_thing_and_display_health(creatng, game.disease_lose_percentage_health * cctrl->max_health / 100, DmgT_Biological, cctrl->disease_caster_plyridx); } }
long creature_add_lair_to_room(struct Thing *creatng, struct Room *room) { struct Thing *lairtng; if (!room_has_enough_free_capacity_for_creature(room, creatng)) return 0; //return _DK_creature_add_lair_to_room(thing, room); // Make sure we don't already have a lair on that position lairtng = find_creature_lair_at_subtile(creatng->mappos.x.stl.num, creatng->mappos.y.stl.num, 0); if (!thing_is_invalid(lairtng)) return 0; struct CreatureStats *crstat; struct CreatureControl *cctrl; crstat = creature_stats_get_from_thing(creatng); cctrl = creature_control_get_from_thing(creatng); room->content_per_model[creatng->model]++; room->used_capacity += crstat->lair_size; if ((cctrl->lair_room_id > 0) && (cctrl->lairtng_idx > 0)) { struct Room *room; room = room_get(cctrl->lair_room_id); creature_remove_lair_from_room(creatng, room); } cctrl->lair_room_id = room->index; // Create the lair thing struct CreatureData *crdata; struct Coord3d pos; pos.x.val = creatng->mappos.x.val; pos.y.val = creatng->mappos.y.val; pos.z.val = creatng->mappos.z.val; crdata = creature_data_get_from_thing(creatng); lairtng = create_object(&pos, crdata->field_1, creatng->owner, -1); if (thing_is_invalid(lairtng)) { ERRORLOG("Could not create lair totem"); remove_thing_from_mapwho(creatng); place_thing_in_mapwho(creatng); return 1; // Return that so we won't try to redo the action over and over } lairtng->mappos.z.val = get_thing_height_at(lairtng, &lairtng->mappos); // Associate creature with the lair cctrl->lairtng_idx = lairtng->index; lairtng->word_13 = creatng->index; lairtng->word_15 = 1; // Lair size depends on creature level lairtng->word_17 = 300 * cctrl->explevel / 20 + 300; lairtng->field_52 = ACTION_RANDOM(0x800); struct Objects *objdat; unsigned long i; objdat = get_objects_data_for_thing(lairtng); i = convert_td_iso(objdat->field_5); set_thing_draw(lairtng, i, objdat->field_7, lairtng->word_15, 0, -1, objdat->field_11); thing_play_sample(creatng, 158, NORMAL_PITCH, 0, 3, 1, 2, FULL_LOUDNESS); create_effect(&pos, imp_spangle_effects[creatng->owner], creatng->owner); anger_set_creature_anger(creatng, 0, AngR_NoLair); remove_thing_from_mapwho(creatng); place_thing_in_mapwho(creatng); return 1; }
short creature_sleep(struct Thing *thing) { //return _DK_creature_sleep(thing); struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); if ((cctrl->slap_turns > 0) || !creature_will_sleep(thing)) { set_start_state(thing); return 0; } struct Room *room; room = get_room_thing_is_on(thing); if (room_is_invalid(room) || (room->kind != RoK_LAIR) || (cctrl->lair_room_id != room->index) || (room->owner != thing->owner)) { set_start_state(thing); return 0; } thing->movement_flags &= ~0x0020; struct CreatureStats *crstat; crstat = creature_stats_get_from_thing(thing); if (((game.play_gameturn + thing->index) % game.recovery_frequency) == 0) { HitPoints recover; recover = compute_creature_max_health(crstat->sleep_recovery, cctrl->explevel); apply_health_to_thing_and_display_health(thing, recover); } anger_set_creature_anger(thing, 0, AngR_NoLair); anger_apply_anger_to_creature(thing, crstat->annoy_sleeping, AngR_Other, 1); if (cctrl->field_82 > 0) { cctrl->field_82--; } if (((game.play_gameturn + thing->index) & 0x3F) == 0) { if (ACTION_RANDOM(100) < 5) { struct Dungeon *dungeon; dungeon = get_dungeon(thing->owner); dungeon->lvstats.backs_stabbed++; } } if (crstat->sleep_exp_slab != SlbT_ROCK) { if (creature_can_gain_experience(thing) && room_has_slab_adjacent(room, crstat->sleep_exp_slab)) { cctrl->exp_points += crstat->sleep_experience; check_experience_upgrade(thing); } } { HitPoints health_max; health_max = compute_creature_max_health(crstat->health, cctrl->explevel); if ((crstat->heal_threshold * health_max / 256 <= thing->health) && (!cctrl->field_82)) { set_start_state(thing); return 1; } } process_lair_enemy(thing, room); return 0; }
short good_attack_room(struct Thing *thing) { // Debug code to find incorrect states if (!is_hero_thing(thing)) { ERRORLOG("Non hero %s index %d owner %d - reset",thing_model_name(thing),(int)thing->index,(int)thing->owner); set_start_state(thing); return 0; } //return _DK_good_attack_room(thing); MapSlabCoord base_slb_x,base_slb_y; base_slb_x = subtile_slab_fast(thing->mappos.x.stl.num); base_slb_y = subtile_slab_fast(thing->mappos.y.stl.num); struct Room *room; room = slab_room_get(base_slb_x, base_slb_y); // If the current tile can be destroyed if (room_exists(room) && (room->owner != thing->owner) && !room_cannot_vandalise(room->kind)) { struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); if (cctrl->instance_id == CrInst_NULL) { set_creature_instance(thing, CrInst_ATTACK_ROOM_SLAB, 1, 0, 0); MapCoord ev_coord_x,ev_coord_y; ev_coord_x = subtile_coord_center(room->central_stl_x); ev_coord_y = subtile_coord_center(room->central_stl_y); event_create_event_or_update_nearby_existing_event(ev_coord_x, ev_coord_y, EvKind_RoomUnderAttack, room->owner, 0); if (is_my_player_number(room->owner)) output_message(SMsg_EnemyDestroyRooms, MESSAGE_DELAY_FIGHT, true); } return 1; } // Otherwise, search around for a tile to destroy long m,n; m = ACTION_RANDOM(SMALL_AROUND_SLAB_LENGTH); for (n=0; n < SMALL_AROUND_SLAB_LENGTH; n++) { MapSlabCoord slb_x,slb_y; slb_x = base_slb_x + (long)small_around[m].delta_x; slb_y = base_slb_y + (long)small_around[m].delta_y; room = slab_room_get(slb_x, slb_y); if (room_exists(room) && (room->owner != thing->owner)) { if (setup_person_move_to_position(thing, slb_x, slb_y, NavRtF_Default)) { thing->continue_state = CrSt_GoodAttackRoom1; return 1; } } m = (m+1) % SMALL_AROUND_SLAB_LENGTH; } set_start_state(thing); return 0; }
TbBool process_prisoner_skelification(struct Thing *thing, struct Room *room) { struct CreatureStats *crstat; crstat = creature_stats_get_from_thing(thing); if ((thing->health >= 0) || (!crstat->humanoid_creature)) { return false; } //TODO [config] Allow skeletification only if spent specific amount of turns in prison (set low value) if (ACTION_RANDOM(101) > game.prison_skeleton_chance) return false; if (is_my_player_number(room->owner)) output_message(SMsg_PrisonMadeSkeleton, 0, true); prison_convert_creature_to_skeleton(room,thing); return true; }
void setup_move_to_new_training_position(struct Thing *thing, struct Room *room, unsigned long restart) { struct CreatureControl *cctrl; struct CreatureStats *crstat; struct Thing *prtng; struct CreatureControl *prctrl; struct Coord3d pos; long i; SYNCDBG(8,"Starting for %s",thing_model_name(thing)); //_DK_setup_move_to_new_training_position(thing, room, a3); cctrl = creature_control_get_from_thing(thing); crstat = creature_stats_get_from_thing(thing); if ( restart ) cctrl->training.search_timeout = 50; // Try partner training if ((crstat->partner_training > 0) && (ACTION_RANDOM(100) < crstat->partner_training)) { prtng = get_creature_in_training_room_which_could_accept_partner(room, thing); if (!thing_is_invalid(prtng)) { SYNCDBG(7,"The %s found %s as training partner.",thing_model_name(thing),thing_model_name(prtng)); prctrl = creature_control_get_from_thing(prtng); prctrl->training.mode = CrTrMd_PartnerTraining; prctrl->training.train_timeout = 75; prctrl->training.partner_idx = thing->index; prctrl->training.partner_creation = thing->creation_turn; cctrl->training.mode = CrTrMd_PartnerTraining; cctrl->training.train_timeout = 75; cctrl->training.partner_idx = prtng->index; cctrl->training.partner_creation = prtng->creation_turn; return; } } // No partner - train at some random position cctrl->training.mode = CrTrMd_SearchForTrainPost; if (find_random_valid_position_for_thing_in_room(thing, room, &pos)) { SYNCDBG(8,"Going to train at (%d,%d)",(int)pos.x.stl.num,(int)pos.y.stl.num); i = get_subtile_number(pos.x.stl.num,pos.y.stl.num); setup_training_move(thing, i); } else { SYNCDBG(8,"No new position found, staying at (%d,%d)",(int)cctrl->moveto_pos.x.stl.num,(int)cctrl->moveto_pos.x.stl.num); } if (cctrl->instance_id == CrInst_NULL) { set_creature_instance(thing, CrInst_SWING_WEAPON_SWORD, 1, 0, 0); } }
void update_player_sounds(void) { int k; struct PlayerInfo *player; SYNCDBG(7,"Starting"); if ((game.operation_flags & GOF_Paused) == 0) { player = get_my_player(); process_messages(); if (!SoundDisabled) { if (game.audiotrack > 0) { PlayMusicPlayer(game.audiotrack); } update_3d_sound_receiver(player); } game.play_gameturn++; } find_nearest_rooms_for_ambient_sound(); process_3d_sounds(); k = (game.bonus_time-game.play_gameturn) / 2; if (bonus_timer_enabled()) { if ((game.bonus_time == game.play_gameturn) || ((game.bonus_time > game.play_gameturn) && (((k <= 100) && ((k % 10) == 0)) || ((k<=300) && ((k % 50) == 0)) || ((k % 250) == 0))) ) play_non_3d_sample(89); } // Rare message easter egg if ((game.play_gameturn != 0) && ((game.play_gameturn % 20000) == 0)) { if (ACTION_RANDOM(2000) == 0) { k = UNSYNC_RANDOM(10); SYNCDBG(9,"Rare message condition met, selected %d",(int)k); if (k == 7) { output_message(SMsg_PantsTooTight, 0, true); } else { output_message(SMsg_FunnyMessages+k, 0, true); } } } SYNCDBG(9,"Finished"); }
/** * Setups a wanderer creature to move to a random thing in given list. * @param first_thing_idx * @param wanderer * @return */ TbBool setup_wanderer_move_to_random_creature_from_list(long first_thing_idx, struct Thing *wanderer) { long possible_targets,target_match; possible_targets = get_wanderer_possible_targets_count_in_list(first_thing_idx,wanderer); // Select random target if (possible_targets < 1) { SYNCDBG(4,"The %s cannot wander to creature, there are no targets",thing_model_name(wanderer)); return false; } target_match = ACTION_RANDOM(possible_targets); if ( wander_to_specific_possible_target_in_list(first_thing_idx, wanderer, target_match) ) { return true; } WARNLOG("The %s index %d cannot wander to creature, it seem all %d creatures were not navigable", thing_model_name(wanderer),(int)wanderer->index,(int)possible_targets); return false; }
void setup_workshop_search_for_post(struct Thing *creatng) { struct Room *room; struct Thing *postng; postng = INVALID_THING; room = get_room_thing_is_on(creatng); // Find a random slab in the room to be used as our starting point long i; unsigned long n; i = ACTION_RANDOM(room->slabs_count); n = room->slabs_list; while (i > 0) { n = get_next_slab_number_in_room(n); i--; } i = room->slabs_count; while (i > 0) { // Loop the slabs list if (n <= 0) { n = room->slabs_list; } MapSlabCoord slb_x, slb_y; slb_x = subtile_slab_fast(stl_num_decode_x(n)); slb_y = subtile_slab_fast(stl_num_decode_y(n)); struct Thing *objtng; objtng = get_workshop_equipment_to_work_with_on_subtile(creatng->owner, slab_subtile_center(slb_x), slab_subtile_center(slb_y)); if (!thing_is_invalid(objtng)) { postng = objtng; } n = get_next_slab_number_in_room(n); i--; } if (thing_is_invalid(postng)) { SYNCDBG(9,"Work in %s, the %s moves to new pos",room_code_name(room->kind),thing_model_name(creatng)); setup_move_to_new_workshop_position(creatng, room, 1); } else { SYNCDBG(9,"Work in %s, the %s found a post",room_code_name(room->kind),thing_model_name(creatng)); setup_workshop_move(creatng, get_subtile_number(postng->mappos.x.stl.num, postng->mappos.y.stl.num)); } }
TbBool setup_head_for_random_unused_lair_slab(struct Thing *creatng, struct Room *room) { SlabCodedCoords slbnum; long n; unsigned long k; n = ACTION_RANDOM(room->slabs_count); slbnum = room->slabs_list; for (k = n; k > 0; k--) { if (slbnum == 0) break; slbnum = get_next_slab_number_in_room(slbnum); } if (slbnum == 0) { ERRORLOG("Taking random slab (%d/%d) in %s index %d failed - internal inconsistency.",(int)n,(int)room->slabs_count,room_code_name(room->kind),(int)room->index); slbnum = room->slabs_list; } k = 0; while (1) { MapSlabCoord slb_x,slb_y; slb_x = slb_num_decode_x(slbnum); slb_y = slb_num_decode_y(slbnum); struct Thing *lairtng; lairtng = find_creature_lair_at_subtile(slab_subtile_center(slb_x), slab_subtile_center(slb_y), 0); if (thing_is_invalid(lairtng)) { if (setup_person_move_to_position(creatng, slab_subtile_center(slb_x), slab_subtile_center(slb_y), NavRtF_Default)) { return true; } WARNLOG("Cannot get somewhere in room"); } slbnum = get_next_slab_number_in_room(slbnum); if (slbnum == 0) { slbnum = room->slabs_list; } k++; if (k >= room->slabs_count) { break; } } return false; }
TbBool creature_find_and_perform_anger_job(struct Thing *creatng) { //return _DK_creature_find_and_perform_anger_job(creatng); struct CreatureStats *crstat; crstat = creature_stats_get_from_thing(creatng); int i, k, n; // Count the amount of jobs set i = 0; k = crstat->jobs_anger; while (k != 0) { if ((k & 1) != 0) i++; k >>= 1; } if (i <= 0) { return false; } // Select a random job as a starting point n = ACTION_RANDOM(i) + 1; i = 0; for (k = 0; k < crtr_conf.angerjobs_count; k++) { if ((crstat->jobs_anger & (1 << k)) != 0) { n--; } if (n <= 0) { i = k; break; } } // Go through all jobs, starting at randomly selected one, attempting to start each one for (k = 0; k < crtr_conf.angerjobs_count; k++) { if ((crstat->jobs_anger & (1 << i)) != 0) { if (attempt_anger_job(creatng, 1 << i)) return 1; } i = (i+1) % crtr_conf.angerjobs_count; } return 0; }
void lightning_modify_palette(struct Thing *thing) { struct PlayerInfo *myplyr; // _DK_lightning_modify_palette(thing); myplyr = get_my_player(); if (thing->health == 0) { PaletteSetPlayerPalette(myplyr, engine_palette); myplyr->field_3 &= ~0x08; return; } if (myplyr->acamera == NULL) { ERRORLOG("No active camera"); return; } if (((thing->health % 8) != 7) && (thing->health != 1) && (ACTION_RANDOM(4) != 0)) { if ((myplyr->field_3 & 0x08) != 0) { if (get_2d_box_distance(&myplyr->acamera->mappos, &thing->mappos) < 11520) { PaletteSetPlayerPalette(myplyr, engine_palette); myplyr->field_3 &= ~0x08; } } return; } if ((myplyr->view_mode != PVM_ParchFadeIn) && (myplyr->view_mode != PVM_ParchFadeOut) && (myplyr->view_mode != PVM_ParchmentView)) { if ((myplyr->field_3 & 0x08) == 0) { if (get_2d_box_distance(&myplyr->acamera->mappos, &thing->mappos) < 11520) { PaletteSetPlayerPalette(myplyr, lightning_palette); myplyr->field_3 |= 0x08; } } } }
short good_back_at_start(struct Thing *thing) { // Debug code to find incorrect states if (!is_hero_thing(thing)) { ERRORLOG("Non hero thing %ld, %s, owner %ld - reset",(long)thing->index,thing_model_name(thing),(long)thing->owner); set_start_state(thing); return false; } //return _DK_good_back_at_start(thing); if (thing->creature.gold_carried <= 0) { set_start_state(thing); return 1; } SubtlCodedCoords stl_num; long m,n; stl_num = get_subtile_number(thing->mappos.x.stl.num,thing->mappos.y.stl.num); m = ACTION_RANDOM(AROUND_MAP_LENGTH); for (n=0; n < AROUND_MAP_LENGTH; n++) { struct Map *mapblk; mapblk = get_map_block_at_pos(stl_num+around_map[m]); // Per-block code if ((mapblk->flags & MapFlg_IsTall) == 0) { MapSubtlCoord stl_x, stl_y; stl_x = stl_num_decode_x(stl_num+around_map[m]); stl_y = stl_num_decode_y(stl_num+around_map[m]); if (setup_person_move_to_position(thing, stl_x, stl_y, NavRtF_Default)) { thing->continue_state = CrSt_GoodDropsGold; return 1; } } // Per-block code ends m = (m + 1) % AROUND_MAP_LENGTH; } set_start_state(thing); return 1; }
struct Room *get_opponent_room(struct Computer2 *comp, PlayerNumber plyr_idx) { static const RoomKind opponent_room_kinds[] = {RoK_DUNGHEART, RoK_PRISON, RoK_LIBRARY, RoK_TREASURE}; struct Dungeon *dungeon; struct Room *room; dungeon = get_players_num_dungeon(plyr_idx); if (dungeon_invalid(dungeon) || (slab_conf.room_types_count < 1)) { return INVALID_ROOM; } int i,n; n = opponent_room_kinds[ACTION_RANDOM(sizeof(opponent_room_kinds)/sizeof(opponent_room_kinds[0]))]; for (i=0; i < slab_conf.room_types_count; i++) { room = room_get(dungeon->room_kind[n]); if (room_exists(room)) { return room; } n = (n + 1) % slab_conf.room_types_count; } return INVALID_ROOM; }
long computer_check_for_expand_room(struct Computer2 *comp, struct ComputerCheck * check) { if (is_newdig_enabled(comp)) return 4; SYNCDBG(8,"Starting"); struct Dungeon *dungeon; dungeon = comp->dungeon; if (dungeon_invalid(dungeon) || !player_has_heart(dungeon->owner)) { SYNCDBG(7,"Computer players %d dungeon in invalid or has no heart",(int)dungeon->owner); return CTaskRet_Unk4; } long around_start; around_start = ACTION_RANDOM(119); // Don't work when placing rooms; we could place in an area for room by mistake if (is_task_in_progress(comp, CTT_PlaceRoom) || is_task_in_progress(comp, CTT_CheckRoomDug)) { SYNCDBG(8,"No rooms expansion - colliding task already in progress"); return CTaskRet_Unk0; } if (computer_player_in_emergency_state(comp)) { SYNCDBG(8,"No rooms expansion - emergency state"); return CTaskRet_Unk0; } if (get_computer_money_less_cost(comp) < dungeon->creatures_total_pay / 3) { SYNCDBG(8,"No rooms expansion - not enough money buffer"); return CTaskRet_Unk0; } const struct ExpandRooms *expndroom; for (expndroom = &expand_rooms[0]; expndroom->rkind != RoK_NONE; expndroom++) { if (computer_check_for_expand_room_kind(comp, check, expndroom->rkind, expndroom->max_slabs, around_start)) { return CTaskRet_Unk1; } } SYNCDBG(8,"No rooms found for expansion"); return CTaskRet_Unk0; }
TbBool attempt_anger_job_persuade(struct Thing *creatng) { struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(creatng); if (cctrl->explevel <= 5) { return false; } if (!can_change_from_state_to(creatng, creatng->active_state, CrSt_CreaturePersuade)) { return false; } struct Dungeon *dungeon; dungeon = get_players_num_dungeon(creatng->owner); int persuade_count; persuade_count = min(dungeon->num_active_creatrs-1, 5); if (persuade_count <= 0) { return false; } persuade_count = ACTION_RANDOM(persuade_count) + 1; if (!external_set_thing_state(creatng, CrSt_CreaturePersuade)) { return false; } cctrl->byte_9A = persuade_count; return true; }
/** * Quick attack is just putting CTA spell on enemy room. * @param comp * @param check */ long computer_check_for_quick_attack(struct Computer2 *comp, struct ComputerCheck * check) { struct Dungeon *dungeon; SYNCDBG(8,"Starting"); //return _DK_computer_check_for_quick_attack(comp, check); dungeon = comp->dungeon; int creatrs_num; creatrs_num = check->param1 * dungeon->num_active_creatrs / 100; if (check->param3 >= creatrs_num) { return 4; } if (computer_able_to_use_magic(comp, PwrK_CALL2ARMS, 1, 3) != 1) { return 4; } if ((check_call_to_arms(comp) != 1) || is_there_an_attack_task(comp)) { return 4; } struct Room *room; room = get_hated_room_for_quick_attack(comp, check->param3); if (room_is_invalid(room)) { return 4; } struct Coord3d pos; // TODO COMPUTER_PLAYER We should make sure the place of cast is accessible for creatures pos.x.val = subtile_coord_center(room->central_stl_x); pos.y.val = subtile_coord_center(room->central_stl_y); pos.z.val = subtile_coord(1,0); if (check->param3 >= count_creatures_availiable_for_fight(comp, &pos)) { return 4; } if (!create_task_magic_support_call_to_arms(comp, &pos, check->param2, 0, creatrs_num)) { return 4; } output_message(SMsg_EnemyHarassments+ACTION_RANDOM(8), 500, 1); return 1; }
TbBool steal_hero(struct PlayerInfo *player, struct Coord3d *pos) { //TODO CONFIG creature models dependency; put them in config files static ThingModel skip_steal_models[] = {6, 7}; static ThingModel prefer_steal_models[] = {3, 12}; struct Thing *herotng; herotng = INVALID_THING; int heronum; struct Dungeon *herodngn; struct CreatureControl *cctrl; unsigned long k; int i; SYNCDBG(8,"Starting"); herodngn = get_players_num_dungeon(game.hero_player_num); k = 0; if (herodngn->num_active_creatrs > 0) { heronum = ACTION_RANDOM(herodngn->num_active_creatrs); i = herodngn->creatr_list_start; SYNCDBG(4,"Selecting random creature %d out of %d heroes",(int)heronum,(int)herodngn->num_active_creatrs); } else { heronum = 0; i = 0; SYNCDBG(4,"No heroes on map, skipping selection"); } while (i != 0) { struct Thing *thing; thing = thing_get(i); TRACE_THING(thing); cctrl = creature_control_get_from_thing(thing); if (thing_is_invalid(thing) || creature_control_invalid(cctrl)) { ERRORLOG("Jump to invalid creature detected"); break; } i = cctrl->players_next_creature_idx; // Thing list loop body TbBool heroallow; heroallow = true; ThingModel skipidx; for (skipidx=0; skipidx < sizeof(skip_steal_models)/sizeof(skip_steal_models[0]); skipidx++) { if (thing->model == skip_steal_models[skipidx]) { heroallow = false; } } if (heroallow) { herotng = thing; } // If we've reached requested hero number, return either current hero on previously selected one if ((heronum <= 0) && thing_is_creature(herotng)) { break; } heronum--; if (i == 0) { i = herodngn->creatr_list_start; } // Thing list loop body ends k++; if (k > CREATURES_COUNT) { ERRORLOG("Infinite loop detected when sweeping creatures list"); erstat_inc(ESE_InfChainTngPerOwner); break; } } if (!thing_is_invalid(herotng)) { move_thing_in_map(herotng, pos); change_creature_owner(herotng, player->id_number); SYNCDBG(3,"Converted %s to owner %d",thing_model_name(herotng),(int)player->id_number); } else { i = ACTION_RANDOM(sizeof(prefer_steal_models)/sizeof(prefer_steal_models[0])); struct Thing *creatng; creatng = create_creature(pos, prefer_steal_models[i], player->id_number); if (thing_is_invalid(creatng)) return false; SYNCDBG(3,"Created %s owner %d",thing_model_name(creatng),(int)player->id_number); } return true; }
TbBool find_place_to_put_door_around_room(const struct Room *room, struct Coord3d *pos) { long m,n; m = ACTION_RANDOM(SMALL_AROUND_SLAB_LENGTH); for (n = 0; n < SMALL_AROUND_SLAB_LENGTH; n++) { // Get position containing room center MapSlabCoord slb_x,slb_y; slb_x = subtile_slab_fast(room->central_stl_x); slb_y = subtile_slab_fast(room->central_stl_y); // Move the position to edge of the room struct Room *sibroom; sibroom = slab_room_get(slb_x, slb_y); while (!room_is_invalid(sibroom) && (sibroom->index == room->index)) { slb_x += small_around[m].delta_x; slb_y += small_around[m].delta_y; sibroom = slab_room_get(slb_x, slb_y); } // Move the position a few tiles further in that direction searching for a place to put door //TODO COMPUTER_PLAYER Why we can only have doors if corridor is at center of the room? This should be fixed to allow doors everywhere around room. int i; for (i = 4; i > 0; i--) { struct SlabMap *slb; slb = get_slabmap_block(slb_x, slb_y); if ((slabmap_owner(slb) != room->owner) || (slb->kind != SlbT_CLAIMED)) { i = 0; break; } if (tag_cursor_blocks_place_door(room->owner, slab_subtile_center(slb_x), slab_subtile_center(slb_y))) { break; } if (!subtile_has_door_thing_on(slab_subtile_center(slb_x), slab_subtile_center(slb_y))) { // No door - the position looks ok break; } slb_x += small_around[m].delta_x; slb_y += small_around[m].delta_y; } // Now if we were able to move, then the position seem ok. One last check - make sure the corridor is not dead end and doesn't already have a door if (i > 0) { MapSlabCoord nxslb_x,nxslb_y; nxslb_x = slb_x + small_around[m].delta_x; nxslb_y = slb_y + small_around[m].delta_y; struct SlabMap *nxslb; nxslb = get_slabmap_block(nxslb_x, nxslb_y); if ((slabmap_owner(nxslb) == room->owner) && (nxslb->kind == SlbT_CLAIMED)) { if (!subtile_has_door_thing_on(slab_subtile_center(nxslb_x), slab_subtile_center(nxslb_y))) { pos->x.val = subtile_coord_center(slab_subtile_center(slb_x)); pos->y.val = subtile_coord_center(slab_subtile_center(slb_y)); pos->z.val = subtile_coord(1,0); return true; } } } m = (m + 1) % SMALL_AROUND_SLAB_LENGTH; } return false; }
TbBool good_creature_setup_task_in_dungeon(struct Thing *creatng, PlayerNumber target_plyr_idx) { struct CreatureControl *cctrl; struct CreatureStats *crstat; cctrl = creature_control_get_from_thing(creatng); SYNCDBG(8,"The %s performing task %d",thing_model_name(creatng), (int)cctrl->party_objective); switch (cctrl->party_objective) { case CHeroTsk_AttackRooms: if (good_setup_attack_rooms(creatng, target_plyr_idx)) { return true; } WARNLOG("Can't attack player %d rooms, switching to attack heart", (int)target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; return false; case CHeroTsk_AttackDnHeart: if (good_setup_wander_to_dungeon_heart(creatng, target_plyr_idx)) { return true; } ERRORLOG("Cannot wander to player %d heart", (int)target_plyr_idx); return false; case CHeroTsk_StealGold: crstat = creature_stats_get_from_thing(creatng); if (creatng->creature.gold_carried < crstat->gold_hold) { if (good_setup_loot_treasure_room(creatng, target_plyr_idx)) { return true; } WARNLOG("Can't loot player %d treasury, switching to attack heart", (int)target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; } else { if (good_setup_wander_to_exit(creatng)) { return true; } WARNLOG("Can't wander to exit after looting player %d treasury, switching to attack heart", (int)target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; } return false; case CHeroTsk_StealSpells: if (!creature_is_dragging_spellbook(creatng)) { if (good_setup_loot_research_room(creatng, target_plyr_idx)) { return true; } WARNLOG("Can't loot player %d spells, switching to attack heart", (int)target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; } else { if (good_setup_wander_to_exit(creatng)) { return true; } WARNLOG("Can't wander to exit after looting player %d spells, switching to attack heart", (int)target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; } return false; case CHeroTsk_AttackEnemies: // Randomly select if we will first try to wander to creature, or to special digger if (ACTION_RANDOM(2) == 1) { // Try wander to creature if (good_setup_wander_to_creature(creatng, cctrl->party.target_plyr_idx)) { SYNCDBG(17,"Finished - wandering to player %d creature", (int)cctrl->party.target_plyr_idx); return true; } // If the wander failed, try wander to special digger if (good_setup_wander_to_spdigger(creatng, cctrl->party.target_plyr_idx)) { SYNCDBG(17,"Finished - wandering to player %d worker", (int)cctrl->party.target_plyr_idx); return true; } } else { // Try wander to special digger if (good_setup_wander_to_spdigger(creatng, cctrl->party.target_plyr_idx)) { SYNCDBG(17,"Finished - wandering to player %d worker", (int)cctrl->party.target_plyr_idx); return true; } // If the wander failed, try wander to creature if (good_setup_wander_to_creature(creatng, cctrl->party.target_plyr_idx)) { SYNCDBG(17,"Finished - wandering to player %d creature", (int)cctrl->party.target_plyr_idx); return true; } } WARNLOG("Can't attack player %d creature, switching to attack heart", (int)cctrl->party.target_plyr_idx); cctrl->party_objective = CHeroTsk_AttackDnHeart; return false; case CHeroTsk_Default: default: SYNCDBG(4,"Wrong task, switching to attack enemies"); cctrl->party_objective = CHeroTsk_AttackEnemies; return false; } }
short researching(struct Thing *thing) { struct Dungeon *dungeon; long i; TRACE_THING(thing); dungeon = get_dungeon(thing->owner); if (is_neutral_thing(thing)) { ERRORLOG("Neutral %s index %d cannot do research",thing_model_name(thing),(int)thing->index); remove_creature_from_work_room(thing); set_start_state(thing); return CrStRet_Unchanged; } if (!creature_can_do_research(thing)) { if (!is_neutral_thing(thing) && (dungeon->current_research_idx < 0)) { if (is_my_player_number(dungeon->owner)) output_message(SMsg_NoMoreReseach, 500, true); } remove_creature_from_work_room(thing); set_start_state(thing); return CrStRet_Unchanged; } // Get and verify working room struct Room *room; room = get_room_thing_is_on(thing); if (creature_work_in_room_no_longer_possible(room, RoK_LIBRARY, thing)) { remove_creature_from_work_room(thing); set_start_state(thing); return CrStRet_ResetFail; } if (room->used_capacity > room->total_capacity) { output_message_room_related_from_computer_or_player_action(room->owner, room->kind, OMsg_RoomTooSmall); remove_creature_from_work_room(thing); set_start_state(thing); return CrStRet_ResetOk; } process_research_function(thing); struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); if ( (game.play_gameturn - dungeon->field_AE5 < 50) && ((game.play_gameturn + thing->index) & 0x03) == 0) { external_set_thing_state(thing, CrSt_CreatureBeHappy); cctrl->countdown_282 = 50; cctrl->long_9A = 0; return CrStRet_Modified; } if (cctrl->instance_id != CrInst_NULL) return 1; cctrl->field_82++; // Shall we do some "Standing and thinking" if (cctrl->field_82 <= 128) { if (cctrl->byte_9A == 3) { // Do some random thinking if ((cctrl->field_82 % 16) == 0) { i = ACTION_RANDOM(LbFPMath_PI) - LbFPMath_PI/2; cctrl->long_9B = ((long)thing->move_angle_xy + i) & LbFPMath_AngleMask; cctrl->byte_9A = 4; } } else { // Look at different direction while thinking if (creature_turn_to_face_angle(thing, cctrl->long_9B) < LbFPMath_PI/18) { cctrl->byte_9A = 3; } } return 1; } // Finished "Standing and thinking" - make "new idea" effect and go to next position if (!setup_random_head_for_room(thing, room, NavRtF_Default)) { ERRORLOG("Cannot move %s index %d in %s room", thing_model_name(thing),(int)thing->index,room_code_name(room->kind)); set_start_state(thing); return 1; } thing->continue_state = CrSt_Researching; cctrl->field_82 = 0; cctrl->byte_9A = 3; if (cctrl->explevel < 3) { create_effect(&thing->mappos, TngEff_Unknown54, thing->owner); } else if (cctrl->explevel < 6) { create_effect(&thing->mappos, TngEff_Unknown55, thing->owner); } else { create_effect(&thing->mappos, TngEff_Unknown56, thing->owner); } return 1; }
short creature_being_scavenged(struct Thing *creatng) { //return _DK_creature_being_scavenged(creatng); struct Thing *fellowtng; struct Dungeon *dungeon; SYNCDBG(8,"Starting"); //return _DK_make_all_players_creatures_angry(plyr_idx); dungeon = get_players_num_dungeon(creatng->owner); fellowtng = INVALID_THING; if (dungeon->num_active_creatrs <= 1) { SYNCDBG(19,"No other creatures"); return 0; } int n; n = ACTION_RANDOM(dungeon->num_active_creatrs-1); unsigned long k; int i; k = 0; i = dungeon->creatr_list_start; while (i != 0) { struct Thing *thing; thing = thing_get(i); TRACE_THING(thing); struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); if (thing_is_invalid(thing) || creature_control_invalid(cctrl)) { ERRORLOG("Jump to invalid creature detected"); break; } i = cctrl->players_next_creature_idx; // Thing list loop body if ((n <= 0) && (thing->index != creatng->index)) { fellowtng = thing; break; } n--; // Thing list loop body ends k++; if (k > CREATURES_COUNT) { ERRORLOG("Infinite loop detected when sweeping creatures list"); break; } } if (thing_is_invalid(fellowtng)) { SYNCDBG(19,"Cannot get creature"); return 0; } if (setup_person_move_to_coord(creatng, &fellowtng->mappos, NavRtF_Default) <= 0) { SYNCDBG(19,"Cannot move to coord"); return 0; } creatng->continue_state = CrSt_CreatureBeingScavenged; if (!S3DEmitterIsPlayingSample(creatng->snd_emitter_id, 156, 0)) thing_play_sample(creatng, 156, NORMAL_PITCH, 0, 3, 1, 2, FULL_LOUDNESS); SYNCDBG(19,"Finished"); return 1; }
/** * Finds a random training post near to the current position of given creature. * Used when finding a training post seems to be taking too long; in that case, creature should start training with a nearest post. * Note that this routine does not always select the nearest post - it is enough if it's 3 subtiles away. * * @param creatng The creature who wish to train with training post. */ void setup_training_search_for_post(struct Thing *creatng) { struct Room *room; struct Thing *traintng; struct Thing *thing; long start_slab; long min_distance,dist; long slb_x,slb_y; long i,k; room = get_room_thing_is_on(creatng); // Let's start from a random slab slb_x = -1; slb_y = -1; min_distance = LONG_MAX; traintng = INVALID_THING; start_slab = ACTION_RANDOM(room->slabs_count); k = start_slab; i = room->slabs_list; while (i != 0) { slb_x = slb_num_decode_x(i); slb_y = slb_num_decode_y(i); i = get_next_slab_number_in_room(i); if (k <= 0) break; k--; } // Got random starting slab, now sweep room slabs from it thing = INVALID_THING; k = room->slabs_count; i = get_slab_number(slb_x,slb_y); while (k > 0) { slb_x = slb_num_decode_x(i); slb_y = slb_num_decode_y(i); i = get_next_slab_number_in_room(i); if (i == 0) i = room->slabs_list; // Per room tile code - find a nearest training post thing = get_object_at_subtile_of_model_and_owned_by(slab_subtile_center(slb_x), slab_subtile_center(slb_y), 31, creatng->owner); if (!thing_is_invalid(thing)) { dist = get_2d_distance(&creatng->mappos, &thing->mappos); if (dist < min_distance) { traintng = thing; min_distance = dist; if (min_distance < (3<<8)) break; } } // Per room tile code ends k--; } // Got trainer (or not...), now do the correct action if (thing_is_invalid(traintng)) { SYNCDBG(6,"Room no longer have training post, moving somewhere else."); setup_move_to_new_training_position(creatng, room, true); } else { i = get_subtile_number(traintng->mappos.x.stl.num,traintng->mappos.y.stl.num); setup_training_move_near(creatng, i); } }