TbBool creature_move_to_using_teleport(struct Thing *thing, struct Coord3d *pos, long walk_speed) { struct CreatureControl *cctrl; short destination_valid; cctrl = creature_control_get_from_thing(thing); if (creature_instance_is_available(thing, CrInst_TELEPORT) && creature_instance_has_reset(thing, CrInst_TELEPORT) && (cctrl->instance_id == CrInst_NULL)) { // Creature can only be teleported to a revealed location destination_valid = true; if (!is_hero_thing(thing) && !is_neutral_thing(thing)) { destination_valid = subtile_revealed(pos->x.stl.num, pos->y.stl.num, thing->owner); } if (destination_valid) { // Use teleport only over large enough distances if (get_2d_box_distance(&thing->mappos, pos) > COORD_PER_STL*game.min_distance_for_teleport) { set_creature_instance(thing, CrInst_TELEPORT, 1, 0, pos); return true; } } } return false; }
short good_leave_through_exit_door(struct Thing *thing) { struct CreatureControl *cctrl; struct Thing *tmptng; // 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); erstat_inc(ESE_BadCreatrState); return false; } //return _DK_good_leave_through_exit_door(thing); tmptng = find_base_thing_on_mapwho(TCls_Object, 49, thing->mappos.x.stl.num, thing->mappos.y.stl.num); if (thing_is_invalid(tmptng)) { return 0; } cctrl = creature_control_get_from_thing(thing); thing->creature.gold_carried = 0; cctrl->field_282 = game.hero_door_wait_time; cctrl->byte_8A = tmptng->creation_turn; place_thing_in_creature_controlled_limbo(thing); internal_set_thing_state(thing, CrSt_GoodWaitInExitDoor); return 1; }
short good_drops_gold(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); erstat_inc(ESE_BadCreatrState); return 0; } //return _DK_good_drops_gold(thing); GoldAmount amount; amount = game.pot_of_gold_holds; if (thing->creature.gold_carried <= game.pot_of_gold_holds) amount = thing->creature.gold_carried; struct Thing *gldtng; gldtng = create_object(&thing->mappos, 6, thing->owner, -1); if (thing_is_invalid(gldtng)) { return 0; } gldtng->valuable.gold_stored = amount; thing->creature.gold_carried -= amount; // Update size of the gold object add_gold_to_pile(gldtng, 0); internal_set_thing_state(thing, CrSt_GoodBackAtStart); return 1; }
TbBool destroy_trap(struct Thing *traptng) { if ((traptng->trap.num_shots == 0) && !is_neutral_thing(traptng) && !is_hero_thing(traptng)) { readd_workshop_item_to_amount_placeable(traptng->owner, traptng->class_id, traptng->model); } delete_thing_structure(traptng, 0); return true; }
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; }
TngUpdateRet update_trap_trigger(struct Thing *traptng) { if (traptng->trap.num_shots <= 0) { return TUFRet_Unchanged; } TbBool do_trig; switch (trap_stats[traptng->model].trigger_type) { case 1: do_trig = update_trap_trigger_line_of_sight_90(traptng); break; case 2: do_trig = update_trap_trigger_pressure(traptng); break; default: ERRORLOG("Illegal trap trigger type %d",(int)trap_stats[traptng->model].trigger_type); do_trig = false; break; } if (do_trig) { const struct ManfctrConfig *mconf; mconf = &game.traps_config[traptng->model]; traptng->trap.long_14t = game.play_gameturn + mconf->shots_delay; int n; n = traptng->trap.num_shots; if ((n > 0) && (n != 255)) { traptng->trap.num_shots = n - 1; if (traptng->trap.num_shots == 0) { // If the trap is in strange location, destroy it after it's depleted struct SlabMap *slb; slb = get_slabmap_thing_is_on(traptng); if ((slb->kind != SlbT_CLAIMED) && (slb->kind != SlbT_PATH)) { traptng->health = -1; } traptng->field_4F &= 0x10; traptng->field_4F |= 0x20; if (!is_neutral_thing(traptng) && !is_hero_thing(traptng)) { remove_workshop_item_from_amount_placeable(traptng->owner, traptng->class_id, traptng->model); } } } return TUFRet_Modified; } return TUFRet_Unchanged; }
short good_wait_in_exit_door(struct Thing *thing) { struct CreatureControl *cctrl; struct Thing *tmptng; // Debug code to find incorrect states if (!is_hero_thing(thing)) { ERRORLOG("Non hero thing %s index %d, owner %d - reset", thing_model_name(thing), (int)thing->index, (int)thing->owner); set_start_state(thing); erstat_inc(ESE_BadCreatrState); return 0; } //return _DK_good_wait_in_exit_door(thing); cctrl = creature_control_get_from_thing(thing); if (cctrl->field_282 <= 0) return 0; cctrl->field_282--; if (cctrl->field_282 == 0) { tmptng = find_base_thing_on_mapwho(TCls_Object, 49, thing->mappos.x.stl.num, thing->mappos.y.stl.num); if (!thing_is_invalid(tmptng)) { if (cctrl->byte_8A == tmptng->creation_turn) { remove_thing_from_creature_controlled_limbo(thing); set_start_state(thing); return 1; } } thing->creature.gold_carried = 0; tmptng = thing_get(cctrl->dragtng_idx); TRACE_THING(tmptng); if (!thing_is_invalid(tmptng)) { delete_thing_structure(tmptng, 0); } kill_creature(thing, INVALID_THING, -1, CrDed_NoEffects|CrDed_NotReallyDying); } return 0; }
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; }
TbBool thing_is_valid_scavenge_target(const struct Thing *calltng, const struct Thing *scavtng) { if (!thing_is_creature(scavtng) || (scavtng->model != calltng->model)) { return false; } if (!is_neutral_thing(scavtng)) { if (!players_are_enemies(calltng->owner, scavtng->owner)) { return false; } } if (thing_is_picked_up(scavtng)) { return false; } if (is_thing_passenger_controlled(scavtng) || creature_is_kept_in_custody(scavtng)) { return false; } if (is_hero_thing(scavtng) && (!gameadd.scavenge_good_allowed)) { return false; } if (is_neutral_thing(scavtng) && (!gameadd.scavenge_neutral_allowed)) { return false; } struct PlayerInfo *scavplyr; scavplyr = INVALID_PLAYER; if (!is_neutral_thing(scavtng)) { scavplyr = get_player(scavtng->owner); } if (scavplyr->controlled_thing_idx != scavtng->index) { struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(scavtng); if (game.play_gameturn - cctrl->temple_cure_gameturn > game.temple_scavenge_protection_turns) { return true; } } return false; }
short good_returns_to_start(struct Thing *thing) { struct Thing *heartng; // Debug code to find incorrect states SYNCDBG(7,"Starting"); 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); erstat_inc(ESE_BadCreatrState); return 0; } //return _DK_good_returns_to_start(thing); heartng = get_player_soul_container(thing->owner); TRACE_THING(heartng); //TODO CREATURE_AI Heroes don't usually have hearts; maybe they should also go back to hero gates, or any room? if (!setup_person_move_to_coord(thing, &heartng->mappos, NavRtF_Default)) { return 0; } thing->continue_state = CrSt_GoodBackAtStart; return 1; }
short good_doing_nothing(struct Thing *creatng) { struct CreatureControl *cctrl; struct PlayerInfo *player; long nturns; PlayerNumber target_plyr_idx; //return _DK_good_doing_nothing(creatng); SYNCDBG(18,"Starting"); TRACE_THING(creatng); // Debug code to find incorrect states if (!is_hero_thing(creatng)) { ERRORLOG("Non hero %s index %d owned by player %d - reset", thing_model_name(creatng),(int)creatng->index,(int)creatng->owner); set_start_state(creatng); return 0; } cctrl = creature_control_get_from_thing(creatng); if (creature_control_invalid(cctrl)) { ERRORLOG("Invalid creature control; no action"); return 0; } // Respect the idle time - just wander around some time nturns = game.play_gameturn - cctrl->idle.start_gameturn; if (nturns <= 1) { return 1; } // Do some wandering also if can't find any task to do if (cctrl->field_5 > (long)game.play_gameturn) { if (creature_choose_random_destination_on_valid_adjacent_slab(creatng)) { creatng->continue_state = CrSt_GoodDoingNothing; } return 1; } // Done wandering - find a target player target_plyr_idx = cctrl->party.target_plyr_idx; if (target_plyr_idx != -1) { player = get_player(target_plyr_idx); if (player_invalid(player)) { ERRORLOG("Invalid target player in %s index %d owned by player %d - reset", thing_model_name(creatng),(int)creatng->index,(int)creatng->owner); cctrl->party.target_plyr_idx = -1; return 0; } if (player->victory_state != VicS_LostLevel) { nturns = game.play_gameturn - cctrl->long_91; if (nturns > 400) { // Go to the previously chosen dungeon if (!creature_can_get_to_dungeon(creatng,target_plyr_idx)) { // Cannot get to the originally selected dungeon - reset it cctrl->party.target_plyr_idx = -1; } } else if (nturns >= 0) { // Waiting - move around a bit if (creature_choose_random_destination_on_valid_adjacent_slab(creatng)) { creatng->continue_state = CrSt_GoodDoingNothing; return 0; } } else { // Value lower than 0 would mean it is invalid WARNLOG("Invalid wait time detected for %s, value %ld",thing_model_name(creatng),(long)cctrl->long_91); cctrl->long_91 = 0; } } else { // The player we've chosen has lost - we'll have to find other target cctrl->party.target_plyr_idx = -1; } } target_plyr_idx = cctrl->party.target_plyr_idx; if (target_plyr_idx == -1) { nturns = game.play_gameturn - cctrl->long_91; if (nturns > 400) { cctrl->long_91 = game.play_gameturn; cctrl->byte_8C = 1; } nturns = game.play_gameturn - cctrl->long_8D; if (nturns > 64) { cctrl->long_8D = game.play_gameturn; cctrl->party.target_plyr_idx = good_find_enemy_dungeon(creatng); } target_plyr_idx = cctrl->party.target_plyr_idx; if (target_plyr_idx == -1) { SYNCDBG(4,"No enemy dungeon to perform %s index %d task", thing_model_name(creatng),(int)creatng->index); if (creature_choose_random_destination_on_valid_adjacent_slab(creatng)) { creatng->continue_state = CrSt_GoodDoingNothing; return 1; } cctrl->field_5 = game.play_gameturn + 16; } return 1; } if (good_creature_setup_task_in_dungeon(creatng, target_plyr_idx)) { return 1; } // If there are problems with the task, do a break before re-trying cctrl->field_5 = game.play_gameturn + 200; return 0; }