long creature_move_direct_line_backwards(struct Thing *thing, struct Coord3d *nextpos, MoveSpeed speed) { if (creature_turn_to_face_backwards(thing, nextpos) > 0) { // Creature is turning - don't let it move creature_set_speed(thing, 0); return 2; } struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); creature_set_speed(thing, -speed); cctrl->flgfield_2 |= TF2_Unkn01; if (get_2d_box_distance(&thing->mappos, nextpos) > -2*cctrl->move_speed) { ERRORDBG(3,"The %s index %d tried to reach (%d,%d) from (%d,%d) with excessive backward speed", thing_model_name(thing),(int)thing->index,(int)nextpos->x.stl.num,(int)nextpos->y.stl.num, (int)thing->mappos.x.stl.num,(int)thing->mappos.y.stl.num); cctrl->moveaccel.x.val = distance_with_angle_to_coord_x(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.y.val = distance_with_angle_to_coord_y(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.z.val = 0; return 1; } else { cctrl->moveaccel.x.val = nextpos->x.val - (MapCoordDelta)thing->mappos.x.val; cctrl->moveaccel.y.val = nextpos->y.val - (MapCoordDelta)thing->mappos.y.val; cctrl->moveaccel.z.val = 0; return 0; } }
TbBool set_door_buildable_and_add_to_amount(PlayerNumber plyr_idx, ThingModel tngmodel, long buildable, long amount) { struct Dungeon *dungeon; if ( (tngmodel <= 0) || (tngmodel >= DOOR_TYPES_COUNT) ) { ERRORDBG(1,"Can't set door availability; invalid door kind %d.",(int)tngmodel); return false; } dungeon = get_dungeon(plyr_idx); if (dungeon_invalid(dungeon)) { ERRORDBG(11,"Can't set door availability; player %d has no dungeon.",(int)plyr_idx); return false; } if (buildable) dungeon->door_build_flags[tngmodel] |= MnfBldF_Manufacturable; dungeon->door_amount_offmap[tngmodel] += amount; dungeon->door_amount_placeable[tngmodel] += amount; if (amount > 0) dungeon->door_build_flags[tngmodel] |= MnfBldF_Built; return true; }
void delaunay_stack_point(long pt_x, long pt_y) { long tri_idx,cor_idx; long dst_tri_idx,dst_cor_idx; long tri_id2, i; NAVIDBG(19,"Starting"); //_DK_delaunay_stack_point(pt_x, pt_y); return; tri_idx = triangle_find8(pt_x << 8, pt_y << 8); if (tri_idx == -1) { NAVIDBG(19,"Tri not found"); return; } delaunay_add_triangle(tri_idx); for (cor_idx=0; cor_idx < 3; cor_idx++) { tri_id2 = Triangles[tri_idx].tags[cor_idx]; if (tri_id2 != -1) { delaunay_add_triangle(tri_id2); } } if (point_find(pt_x, pt_y, &dst_tri_idx, &dst_cor_idx)) { tri_idx = dst_tri_idx; cor_idx = dst_cor_idx; unsigned long k; k = 0; do { tri_id2 = Triangles[tri_idx].tags[cor_idx]; if (tri_id2 == -1) { NAVIDBG(19,"Tag not found"); break; } i = link_find(tri_id2, tri_idx); if (i == -1) { NAVIDBG(19,"Link not found"); break; } cor_idx = MOD3[i+1]; tri_idx = tri_id2; delaunay_add_triangle(tri_idx); k++; if (k >= TRIANLGLES_COUNT) { ERRORDBG(9,"Infinite loop detected"); break; } } while (tri_idx != dst_tri_idx); } NAVIDBG(19,"Done"); }
struct Thing *create_cave_in(struct Coord3d *pos, unsigned short cimodel, unsigned short owner) { struct MagicStats *magstat; struct Dungeon *dungeon; struct Thing *thing; if ( !i_can_allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots) ) { ERRORDBG(3,"Cannot create cave in %d for player %d. There are too many things allocated.",(int)cimodel,(int)owner); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } thing = allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots); if (thing->index == 0) { ERRORDBG(3,"Should be able to allocate cave in %d for player %d, but failed.",(int)cimodel,(int)owner); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } thing->class_id = TCls_CaveIn; thing->model = 0; thing->parent_idx = thing->index; memcpy(&thing->mappos,pos,sizeof(struct Coord3d)); thing->owner = owner; thing->creation_turn = game.play_gameturn; magstat = &game.keeper_power_stats[PwrK_CAVEIN]; thing->word_15 = magstat->time; thing->byte_13 = pos->x.stl.num; thing->byte_14 = pos->y.stl.num; thing->byte_17 = cimodel; thing->health = magstat->time; if (owner != game.neutral_player_num) { dungeon = get_dungeon(owner); dungeon->camera_deviate_quake = thing->word_15; } add_thing_to_its_class_list(thing); place_thing_in_mapwho(thing); return thing; }
long tree_to_route(long tag_start_id, long tag_end_id, long *route_pts) { long ipt; if (tag_current != Tags[tag_start_id]) return -1; ipt = copy_tree_to_route(tag_start_id, tag_end_id, route_pts, 3000+1); if (ipt < 0) { erstat_inc(ESE_BadRouteTree); ERRORDBG(6,"route length overflow"); } return ipt; }
struct Thing *create_ambient_sound(const struct Coord3d *pos, ThingModel model, PlayerNumber owner) { struct Thing *thing; if ( !i_can_allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots) ) { ERRORDBG(3,"Cannot create ambient sound %d for player %d. There are too many things allocated.",(int)model,(int)owner); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } thing = allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots); if (thing->index == 0) { ERRORDBG(3,"Should be able to allocate ambient sound %d for player %d, but failed.",(int)model,(int)owner); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } thing->class_id = TCls_AmbientSnd; thing->model = model; thing->parent_idx = thing->index; memcpy(&thing->mappos,pos,sizeof(struct Coord3d)); thing->owner = owner; thing->field_4F |= TF4F_DoNotDraw; add_thing_to_its_class_list(thing); return thing; }
long light_create_light(struct InitLight *ilght) { struct Light *lgt; struct ShadowCache *shdc; unsigned long k; lgt = light_allocate_light(); if (light_is_invalid(lgt)) { return 0; } if (ilght->is_dynamic) { shdc = light_allocate_shadow_cache(); if (light_shadow_cache_invalid(shdc)) { ERRORDBG(11,"Cannot allocate cache for dynamic light"); light_free_light(lgt); return 0; } light_total_dynamic_lights++; lgt->shadow_index = light_shadow_cache_index(shdc); light_add_light_to_list(lgt, &game.thing_lists[TngList_DynamLights]); } else { light_total_stat_lights++; light_add_light_to_list(lgt, &game.thing_lists[TngList_StaticLights]); stat_light_needs_updating = 1; } lgt->flags |= LgtF_Unkn02; lgt->flags |= LgtF_Unkn08; lgt->mappos.x.val = ilght->mappos.x.val; lgt->mappos.y.val = ilght->mappos.y.val; lgt->mappos.z.val = ilght->mappos.z.val; lgt->field_16 = ilght->field_0; lgt->field_2 = ilght->field_2; k = 2 * ilght->field_3; lgt->field_1 = k ^ ((k ^ lgt->field_1) & 0x01); set_flag_byte(&lgt->flags,LgtF_Dynamic,ilght->is_dynamic); lgt->field_1A = ilght->field_8; lgt->field_18 = ilght->field_4; lgt->field_12 = ilght->field_12; return lgt->index; }
/** * Counts amount of rooms of specific type owned by specific player. * @param plyr_idx The player number. Only specific player number is accepted. * @param rkind Room kind to count. Only specific kind is accepted. */ long count_player_rooms_of_type(PlayerNumber plyr_idx, RoomKind rkind) { struct Dungeon *dungeon; struct Room *room; long i; unsigned long k; // note that we can't get_players_num_dungeon() because players // may be uninitialized yet when this is called. dungeon = get_dungeon(plyr_idx); if (dungeon_invalid(dungeon)) return 0; i = dungeon->room_kind[rkind]; k = 0; while (i != 0) { room = room_get(i); if (room_is_invalid(room)) { ERRORLOG("Jump to invalid room detected"); break; } i = room->next_of_owner; // No Per-room code - we only want count SYNCDBG(19,"Player %d has %s at (%d,%d)",(int)plyr_idx, room_code_name(room->kind), (int)room->central_stl_x, (int)room->central_stl_y); if (room->owner != plyr_idx) { ERRORDBG(3,"Player %d has bad room in %s list; it's really %s index %d owned by player %d",(int)plyr_idx, room_code_name(rkind), room_code_name(room->kind), (int)room->index, (int)room->owner); break; } k++; if (k > ROOMS_COUNT) { ERRORLOG("Infinite loop detected when sweeping rooms list"); break; } } return k; }
TbBool sibling_line_of_sight_ignoring_door(const struct Coord3d *prevpos, const struct Coord3d *nextpos, const struct Thing *doortng) { // If we don't want to ignore any doors if (thing_is_invalid(doortng)) { // Check for door at central subtile if (subtile_is_door(stl_slab_center_subtile(nextpos->x.stl.num),stl_slab_center_subtile(nextpos->y.stl.num))) { return false; } } // If only one dimensions changed, allow the pass // (in that case the outcome has been decided before this call) if ((nextpos->x.stl.num == prevpos->x.stl.num) || (nextpos->y.stl.num == prevpos->y.stl.num)) { // change is (x,0) or (0,x) return true; } struct Coord3d posmvx; struct Coord3d posmvy; MapSubtlDelta subdelta_x, subdelta_y; subdelta_x = (nextpos->x.stl.num - (MapSubtlDelta)prevpos->x.stl.num); subdelta_y = (nextpos->y.stl.num - (MapSubtlDelta)prevpos->y.stl.num); switch (subdelta_x + 2 * subdelta_y) { case -3: posmvx.x.val = prevpos->x.val - COORD_PER_STL; posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val - COORD_PER_STL; posmvy.z.val = prevpos->z.val; if (point_in_map_is_solid_ignoring_door(&posmvx, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) X",(int)posmvx.x.stl.num,(int)posmvx.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } if (point_in_map_is_solid_ignoring_door(&posmvy, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) Y",(int)posmvy.x.stl.num,(int)posmvy.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } break; case -1: posmvx.x.val = prevpos->x.val + COORD_PER_STL; posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val - COORD_PER_STL; posmvy.z.val = prevpos->z.val; if (point_in_map_is_solid_ignoring_door(&posmvx, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) X",(int)posmvx.x.stl.num,(int)posmvx.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } if (point_in_map_is_solid_ignoring_door(&posmvy, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) Y",(int)posmvy.x.stl.num,(int)posmvy.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } break; case 1: posmvx.x.val = prevpos->x.val - COORD_PER_STL; posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val + COORD_PER_STL; posmvy.z.val = prevpos->z.val; if (point_in_map_is_solid_ignoring_door(&posmvx, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) X",(int)posmvx.x.stl.num,(int)posmvx.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } if (point_in_map_is_solid_ignoring_door(&posmvy, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) Y",(int)posmvy.x.stl.num,(int)posmvy.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } break; case 3: posmvx.x.val = prevpos->x.val + COORD_PER_STL; posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val + COORD_PER_STL; posmvy.z.val = prevpos->z.val; if (point_in_map_is_solid_ignoring_door(&posmvx, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) X",(int)posmvx.x.stl.num,(int)posmvx.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } if (point_in_map_is_solid_ignoring_door(&posmvy, doortng)) { SYNCDBG(17, "Cannot see through (%d,%d) with delta (%d,%d) Y",(int)posmvy.x.stl.num,(int)posmvy.y.stl.num,(int)subdelta_x,(int)subdelta_y); return false; } break; default: ERRORDBG(8,"Invalid use of sibling function, delta (%d,%d)",(int)subdelta_x,(int)subdelta_y); break; } return true; }
TbBool sibling_line_of_sight_3d_including_lava_check_ignoring_own_door(const struct Coord3d *prevpos, const struct Coord3d *nextpos, PlayerNumber plyr_idx) { // Check for door at central subtile if (subtile_is_door(stl_slab_center_subtile(nextpos->x.stl.num), stl_slab_center_subtile(nextpos->y.stl.num))) { return false; } // If only one dimensions changed, allow the pass // (in that case the outcome has been decided before this call) if ((nextpos->x.stl.num == prevpos->x.stl.num) || (nextpos->y.stl.num == prevpos->y.stl.num)) { // change is (x,0) or (0,x) return true; } struct Coord3d posmvy; struct Coord3d posmvx; int subdelta_x, subdelta_y; subdelta_x = (nextpos->x.stl.num - (MapSubtlDelta)prevpos->x.stl.num); subdelta_y = (nextpos->y.stl.num - (MapSubtlDelta)prevpos->y.stl.num); switch (subdelta_x + 2 * subdelta_y) { case -3: // change is (-1,-1) posmvx.x.val = prevpos->x.val - subtile_coord(1,0); posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val - subtile_coord(1,0); posmvy.z.val = prevpos->z.val; if (get_point_in_map_solid_flags_ignoring_own_door(&posmvy, plyr_idx) & 0x01) { return false; } if (get_point_in_map_solid_flags_ignoring_own_door(&posmvx, plyr_idx) & 0x01) { return false; } break; case -1: // change is (1,-1) as (-1,0) was eliminated earlier posmvx.x.val = prevpos->x.val + subtile_coord(1,0); posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val - subtile_coord(1,0); posmvy.z.val = prevpos->z.val; if (get_point_in_map_solid_flags_ignoring_own_door(&posmvy, plyr_idx) & 0x01) { return false; } if (get_point_in_map_solid_flags_ignoring_own_door(&posmvx, plyr_idx) & 0x01) { return false; } break; case 1: // change is (-1,1) as (1,0) was eliminated earlier posmvx.x.val = prevpos->x.val - subtile_coord(1,0); posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val + subtile_coord(1,0); posmvy.z.val = prevpos->z.val; if (get_point_in_map_solid_flags_ignoring_own_door(&posmvy, plyr_idx) & 0x01) { return false; } if (get_point_in_map_solid_flags_ignoring_own_door(&posmvx, plyr_idx) & 0x01) { return false; } break; case 3: // change is (1,1) posmvx.x.val = prevpos->x.val + subtile_coord(1,0); posmvx.y.val = prevpos->y.val; posmvx.z.val = prevpos->z.val; posmvy.x.val = prevpos->x.val; posmvy.y.val = prevpos->y.val + subtile_coord(1,0); posmvy.z.val = prevpos->z.val; if (get_point_in_map_solid_flags_ignoring_own_door(&posmvy, plyr_idx) & 0x01) { return false; } if (get_point_in_map_solid_flags_ignoring_own_door(&posmvx, plyr_idx) & 0x01) { return false; } break; default: ERRORDBG(8,"Invalid use of sibling function, delta (%d,%d)",(int)subdelta_x,(int)subdelta_y); break; } return true; }
long creature_move_to_using_gates(struct Thing *thing, struct Coord3d *pos, MoveSpeed speed, long a4, NaviRouteFlags flags, TbBool backward) { struct Coord3d nextpos; AriadneReturn follow_result; long i; SYNCDBG(18,"Starting to move %s index %d from (%d,%d) to (%d,%d) with speed %d",thing_model_name(thing), (int)thing->index,(int)thing->mappos.x.stl.num,(int)thing->mappos.y.stl.num,(int)pos->x.stl.num,(int)pos->y.stl.num,(int)speed); TRACE_THING(thing); if ( backward ) { // Rotate the creature 180 degrees to trace route with forward move i = (thing->move_angle_xy + LbFPMath_PI); thing->move_angle_xy = i & LbFPMath_AngleMask; } follow_result = creature_follow_route_to_using_gates(thing, pos, &nextpos, speed, flags); SYNCDBG(18,"The %s index %d route result: %d, next pos (%d,%d)",thing_model_name(thing),(int)thing->index,(int)follow_result,(int)nextpos.x.stl.num,(int)nextpos.y.stl.num); if ( backward ) { // Rotate the creature back i = (thing->move_angle_xy + LbFPMath_PI); thing->move_angle_xy = i & LbFPMath_AngleMask; } if ((follow_result == AridRet_PartOK) || (follow_result == AridRet_Val2)) { creature_set_speed(thing, 0); return -1; } if (follow_result == AridRet_FinalOK) { return 1; } struct CreatureControl *cctrl; cctrl = creature_control_get_from_thing(thing); if ( backward ) { if (creature_turn_to_face_backwards(thing, &nextpos) > 0) { // Creature is turning - don't let it move creature_set_speed(thing, 0); } else { creature_set_speed(thing, -speed); cctrl->flgfield_2 |= TF2_Unkn01; if (get_2d_box_distance(&thing->mappos, &nextpos) > -2*cctrl->move_speed) { ERRORDBG(3,"The %s index %d tried to reach (%d,%d) from (%d,%d) with excessive backward speed", thing_model_name(thing),(int)thing->index,(int)nextpos.x.stl.num,(int)nextpos.y.stl.num, (int)thing->mappos.x.stl.num,(int)thing->mappos.y.stl.num); cctrl->moveaccel.x.val = distance_with_angle_to_coord_x(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.y.val = distance_with_angle_to_coord_y(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.z.val = 0; } else { cctrl->moveaccel.x.val = nextpos.x.val - (MapCoordDelta)thing->mappos.x.val; cctrl->moveaccel.y.val = nextpos.y.val - (MapCoordDelta)thing->mappos.y.val; cctrl->moveaccel.z.val = 0; } } SYNCDBG(18,"Backward target set, speed %d, accel (%d,%d)",(int)cctrl->move_speed,(int)cctrl->moveaccel.x.val,(int)cctrl->moveaccel.y.val); } else { if (creature_turn_to_face(thing, &nextpos) > 0) { // Creature is turning - don't let it move creature_set_speed(thing, 0); } else { creature_set_speed(thing, speed); cctrl->flgfield_2 |= TF2_Unkn01; if (get_2d_box_distance(&thing->mappos, &nextpos) > 2*cctrl->move_speed) { ERRORDBG(3,"The %s index %d tried to reach (%d,%d) from (%d,%d) with excessive forward speed", thing_model_name(thing),(int)thing->index,(int)nextpos.x.stl.num,(int)nextpos.y.stl.num, (int)thing->mappos.x.stl.num,(int)thing->mappos.y.stl.num); cctrl->moveaccel.x.val = distance_with_angle_to_coord_x(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.y.val = distance_with_angle_to_coord_y(cctrl->move_speed, thing->move_angle_xy); cctrl->moveaccel.z.val = 0; } else { cctrl->moveaccel.x.val = nextpos.x.val - (MapCoordDelta)thing->mappos.x.val; cctrl->moveaccel.y.val = nextpos.y.val - (MapCoordDelta)thing->mappos.y.val; cctrl->moveaccel.z.val = 0; } } SYNCDBG(18,"Forward target set, speed %d, accel (%d,%d)",(int)cctrl->move_speed,(int)cctrl->moveaccel.x.val,(int)cctrl->moveaccel.y.val); } return 0; }
struct Thing *create_trap(struct Coord3d *pos, ThingModel trpkind, PlayerNumber plyr_idx) { SYNCDBG(7,"Starting"); struct TrapStats *trapstat; trapstat = &trap_stats[trpkind]; if (!i_can_allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots)) { ERRORDBG(3,"Cannot create trap %s for player %d. There are too many things allocated.",trap_code_name(trpkind),(int)plyr_idx); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } struct InitLight ilght; LbMemorySet(&ilght, 0, sizeof(struct InitLight)); struct Thing *thing; thing = allocate_free_thing_structure(FTAF_FreeEffectIfNoSlots); if (thing->index == 0) { ERRORDBG(3,"Should be able to allocate trap %s for player %d, but failed.",trap_code_name(trpkind),(int)plyr_idx); erstat_inc(ESE_NoFreeThings); return INVALID_THING; } thing->class_id = TCls_Trap; thing->model = trpkind; thing->mappos.x.val = pos->x.val; thing->mappos.y.val = pos->y.val; thing->mappos.z.val = pos->z.val; thing->next_on_mapblk = 0; thing->parent_idx = thing->index; thing->owner = plyr_idx; char start_frame; if (trapstat->field_13) { start_frame = -1; } else { start_frame = 0; } set_thing_draw(thing, trapstat->field_4, trapstat->field_D, trapstat->field_8, trapstat->field_C, start_frame, 2); if (trapstat->field_11) { thing->field_4F |= 0x02; } else { thing->field_4F &= ~0x02; } if (trapstat->field_C) { thing->field_4F |= 0x40; } else { thing->field_4F &= ~0x40; } thing->clipbox_size_xy = trapstat->size_xy; thing->clipbox_size_yz = trapstat->field_16; thing->solid_size_xy = trapstat->size_xy; thing->field_5C = trapstat->field_16; thing->creation_turn = game.play_gameturn; thing->health = trapstat->field_0; thing->field_4F &= ~0x10; thing->field_4F |= 0x20; thing->byte_13 = 0; thing->long_14 = game.play_gameturn; if (trapstat->field_1C != 0) { ilght.mappos.x.val = thing->mappos.x.val; ilght.mappos.y.val = thing->mappos.y.val; ilght.mappos.z.val = thing->mappos.z.val; ilght.field_0 = trapstat->field_1C; ilght.field_2 = trapstat->field_1E; ilght.is_dynamic = 1; ilght.field_3 = trapstat->field_1F; thing->light_id = light_create_light(&ilght); if (thing->light_id <= 0) { SYNCDBG(8,"Cannot allocate dynamic light to %s.",thing_model_name(thing)); } } add_thing_to_its_class_list(thing); place_thing_in_mapwho(thing); return thing; }