struct Thing *create_crate_in_workshop(struct Room *room, ThingModel cratngmodel, MapSubtlCoord stl_x, MapSubtlCoord stl_y) { struct Coord3d pos; struct Thing *cratetng; if (!room_role_matches(room->kind, RoRoF_CratesStorage)) { SYNCDBG(4,"Crate %s cannot be created in a %s owned by player %d, wrong room",object_code_name(cratngmodel),room_code_name(room->kind),(int)room->owner); return INVALID_THING; } pos.x.val = subtile_coord_center(stl_x); pos.y.val = subtile_coord_center(stl_y); pos.z.val = 0; cratetng = create_object(&pos, cratngmodel, room->owner, -1); if (thing_is_invalid(cratetng)) { return INVALID_THING; } // Neutral thing do not need any more processing if (is_neutral_thing(cratetng) || !player_exists(get_player(room->owner))) { return cratetng; } if (!add_workshop_object_to_workshop(room, cratetng)) { ERRORLOG("Could not fit %s in %s index %d", thing_model_name(cratetng),room_code_name(room->kind),(int)room->index); //remove_item_from_room_capacity(room); -- no need, it was not added destroy_object(cratetng); return INVALID_THING; } ThingClass tngclass; ThingModel tngmodel; tngclass = crate_thing_to_workshop_item_class(cratetng); tngmodel = crate_thing_to_workshop_item_model(cratetng); add_workshop_item_to_amounts(cratetng->owner, tngclass, tngmodel); return cratetng; }
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; }
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; }
TbBool creature_can_do_scavenging(const struct Thing *creatng) { if (is_neutral_thing(creatng)) { return false; } struct CreatureStats *crstat; crstat = creature_stats_get_from_thing(creatng); return (crstat->scavenge_value > 0); }
/** * Returns if given creature is able to heal by sleeping. * Does not take into consideration if the creature has a lair, checks only if * the creature model is able to heal in its lair in general. * @param creatng * @return */ TbBool creature_can_do_healing_sleep(const struct Thing *creatng) { if (is_neutral_thing(creatng)) { return false; } struct CreatureStats *crstat; crstat = creature_stats_get_from_thing(creatng); return ((crstat->heal_requirement > 0) && (crstat->lair_size > 0)); }
TbBool creature_can_do_research(const struct Thing *creatng) { if (is_neutral_thing(creatng)) { return false; } struct CreatureStats *crstat; struct Dungeon *dungeon; crstat = creature_stats_get_from_thing(creatng); dungeon = get_dungeon(creatng->owner); return (crstat->research_value > 0) && (dungeon->current_research_idx >= 0); }
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; }
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; }
TbBool creature_can_do_research_near_pos(const struct Thing *creatng, MapSubtlCoord stl_x, MapSubtlCoord stl_y, CreatureJob new_job, unsigned long flags) { if (!creature_can_do_research(creatng)) { struct Room *room; room = subtile_room_get(stl_x, stl_y); struct Dungeon *dungeon; dungeon = get_dungeon(room->owner); if (!is_neutral_thing(creatng) && (dungeon->current_research_idx < 0)) { if (is_my_player_number(dungeon->owner) && ((flags & JobChk_PlayMsgOnFail) != 0)) { output_message(SMsg_NoMoreReseach, 500, true); } } return false; } return true; }
TbBool find_pressure_trigger_trap_target_passing_by_subtile(const struct Thing *traptng, MapSubtlCoord stl_x, MapSubtlCoord stl_y, struct Thing **found_thing) { struct Thing *thing; struct Map *mapblk; long i; unsigned long k; mapblk = get_map_block_at(stl_x,stl_y); k = 0; i = get_mapwho_thing_index(mapblk); while (i != 0) { thing = thing_get(i); TRACE_THING(thing); if (thing_is_invalid(thing)) { ERRORLOG("Jump to invalid thing detected"); break; } i = thing->next_on_mapblk; // Per thing code start if (thing_is_creature(thing) && (thing->owner != traptng->owner)) { if (!creature_is_being_unconscious(thing) && !thing_is_dragged_or_pulled(thing) && !creature_is_kept_in_custody_by_enemy(thing) && !creature_is_dying(thing) && ((get_creature_model_flags(thing) & CMF_IsSpectator) == 0)) { if (!is_neutral_thing(thing) && !players_are_mutual_allies(traptng->owner,thing->owner)) { *found_thing = thing; return true; } } } // Per thing code end k++; if (k > THINGS_COUNT) { ERRORLOG("Infinite loop detected when sweeping things list"); break; } } return false; }
TbBool update_trap_trigger_line_of_sight_90_on_subtile(struct Thing *traptng, MapSubtlCoord stl_x, MapSubtlCoord stl_y) { struct Thing *thing; struct Map *mapblk; long i; unsigned long k; mapblk = get_map_block_at(stl_x,stl_y); k = 0; i = get_mapwho_thing_index(mapblk); while (i != 0) { thing = thing_get(i); TRACE_THING(thing); if (thing_is_invalid(thing)) { ERRORLOG("Jump to invalid thing detected"); break; } i = thing->next_on_mapblk; // Per thing code start if (thing_is_creature(thing) && (thing->owner != traptng->owner)) { // Trigger for enemy player, or any player for neutral traps (otherwise neutral traps would be useless) if (players_are_enemies(traptng->owner,thing->owner) || is_neutral_thing(traptng)) { if (!creature_is_being_unconscious(thing) && !thing_is_dragged_or_pulled(thing) && !creature_is_kept_in_custody_by_enemy(thing) && !creature_is_dying(thing) && ((get_creature_model_flags(thing) & CMF_IsSpectator) == 0)) { activate_trap(traptng, thing); return true; } } } // Per thing code end k++; if (k > THINGS_COUNT) { ERRORLOG("Infinite loop detected when sweeping things list"); break; } } return false; }
short at_research_room(struct Thing *thing) { struct CreatureControl *cctrl; struct Dungeon *dungeon; struct Room *room; cctrl = creature_control_get_from_thing(thing); cctrl->target_room_id = 0; dungeon = get_dungeon(thing->owner); 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); } set_start_state(thing); return 0; } room = get_room_thing_is_on(thing); if (!room_initially_valid_as_type_for_thing(room, RoK_LIBRARY, thing)) { WARNLOG("Room %s owned by player %d is invalid for %s index %d",room_code_name(room->kind),(int)room->owner,thing_model_name(thing),(int)thing->index); set_start_state(thing); return 0; } if (!add_creature_to_work_room(thing, room)) { set_start_state(thing); return 0; } if (!setup_random_head_for_room(thing, room, NavRtF_Default)) { ERRORLOG("The %s index %d can not move in research room", thing_model_name(thing),(int)thing->index); remove_creature_from_work_room(thing); set_start_state(thing); return 0; } thing->continue_state = CrSt_Researching; cctrl->field_82 = 0; cctrl->byte_9A = 3; return 1; }
long get_explore_sight_distance_in_slabs(const struct Thing *thing) { struct PlayerInfo *player; if (!thing_exists(thing)) { return 0; } if (is_neutral_thing(thing)) { return 7; } player = get_player(thing->owner); long dist; if (player->controlled_thing_idx != thing->index) { dist = 7; } else { dist = get_creature_can_see_subtiles() / STL_PER_SLB; if (dist <= 7) dist = 7; } return dist; }
TbBool process_scavenge_creature_from_level(struct Thing *scavtng, struct Thing *calltng, long work_value) { struct Dungeon *calldngn; long num_prayers; calldngn = get_dungeon(calltng->owner); if (dungeon_invalid(calldngn)) { ERRORLOG("The %s owner %d can't do scavenging - has no dungeon",thing_model_name(calltng),(int)calltng->owner); return false; } // Compute amount of creatures praying against the scavenge if (!is_neutral_thing(scavtng)) { struct Dungeon *scavdngn; scavdngn = get_dungeon(scavtng->owner); num_prayers = scavdngn->creatures_praying[scavtng->model]; } else { num_prayers = 0; } // Increase scavenging counter, used to break the prayers counter calldngn->creatures_scavenging[scavtng->model]++; // If scavenge is blocked by prayers, return if (calldngn->creatures_scavenging[calltng->model] < 2 * num_prayers) { SYNCDBG(8, "Player %d prayers (%d) are blocking player %d scavenging (%d) of %s", (int)scavtng->owner, (int)num_prayers, (int)calltng->owner, (int)calldngn->creatures_scavenging[calltng->model], thing_model_name(calltng)); return false; } SYNCDBG(18,"The %s index %d scavenges %s index %d",thing_model_name(calltng),(int)calltng->index,thing_model_name(scavtng),(int)scavtng->index); // If we're starting to scavenge a new creature, do the switch if (calldngn->scavenge_targets[calltng->model] != scavtng->index) { calldngn->scavenge_turn_points[calltng->model] = work_value; if (calldngn->scavenge_targets[calltng->model] > 0) { // Stop scavenging old creature struct Thing *thing; thing = thing_get(calldngn->scavenge_targets[calltng->model]); if (thing_is_creature(thing) && (thing->model == calltng->model)) { if (creature_is_being_scavenged(thing)) { set_start_state(thing); } } } // Start the new scavenging calldngn->scavenge_targets[calltng->model] = scavtng->index; if (is_my_player_number(scavtng->owner)) { output_message(SMsg_CreatureScanvenged, 500, 1); } event_create_event(scavtng->mappos.x.val, scavtng->mappos.y.val, EvKind_CreatrScavenged, scavtng->owner, scavtng->index); } else { calldngn->scavenge_turn_points[calltng->model] += work_value; } // Make sure the scavenged creature is in correct state if (!creature_is_being_scavenged(scavtng)) { if (!is_neutral_thing(scavtng)) { external_set_thing_state(scavtng, CrSt_CreatureBeingScavenged); } } long scavpts; scavpts = calculate_correct_creature_scavenge_required(scavtng, calltng->owner); if ((scavpts << 8) < calldngn->scavenge_turn_points[calltng->model]) { SYNCDBG(8,"The %s index %d owner %d accumulated enough points to turn to scavenger",thing_model_name(scavtng),(int)scavtng->index,(int)scavtng->owner); turn_creature_to_scavenger(scavtng, calltng); calldngn->scavenge_turn_points[calltng->model] -= (scavpts << 8); return true; } return false; }
struct Thing *find_prisoner_for_thing(struct Thing *creatng) { struct CreatureControl *cctrl; struct Thing *thing; unsigned long k; long i; TRACE_THING(creatng); struct Room *room; room = INVALID_ROOM; if (!is_neutral_thing(creatng)) { room = find_nearest_room_for_thing_with_used_capacity(creatng, creatng->owner, RoK_PRISON, NavRtF_Default, 1); } if (room_exists(room)) { i = room->creatures_list; } else { i = 0; } struct Thing *out_creatng; long out_delay; out_creatng = INVALID_THING; out_delay = LONG_MAX; k = 0; while (i != 0) { thing = thing_get(i); TRACE_THING(thing); cctrl = creature_control_get_from_thing(thing); if (!creature_control_exists(cctrl)) { ERRORLOG("Jump to invalid creature %ld detected",i); break; } i = cctrl->next_in_room; // Per creature code long dist, durt; dist = get_2d_box_distance(&creatng->mappos, &thing->mappos); if (out_delay < 0) { // If we have a victim which isn't frozen, accept only other unfrozen creatures if ((dist <= LONG_MAX) && !creature_affected_by_spell(thing, SplK_Freeze)) { out_creatng = thing; out_delay = -1; } } else if (creature_affected_by_spell(thing, SplK_Freeze)) { // If the victim is frozen, select one which will unfreeze sooner durt = get_spell_duration_left_on_thing(thing, SplK_Freeze); if ((durt > 0) && (out_delay > durt)) { out_creatng = thing; out_delay = durt; } } else { // Found first unfrozen victim - change out_delay to mark thet we no longer want frozen ones out_creatng = thing; out_delay = -1; } // Per creature code ends k++; if (k > THINGS_COUNT) { ERRORLOG("Infinite loop detected when sweeping creatures list"); break; } } return out_creatng; }
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; }