/** * @brief Add a callback of a function into a node event. There can be more than on listener. * @param[in,out] node The node to add the listener to. * @param[in] property The property of the node to add the listener to. * @param[in] functionNode The node of the listener callback. */ void UI_AddListener (uiNode_t *node, const value_t *property, const uiNode_t *functionNode) { if (node->dynamic) { Com_Printf("UI_AddListener: '%s' is a dynamic node. We can't listen it.\n", UI_GetPath(node)); return; } if (functionNode->dynamic) { Com_Printf("UI_AddListener: '%s' is a dynamic node. It can't be a listener callback.\n", UI_GetPath(functionNode)); return; } /* create the call action */ uiAction_t* const action = Mem_PoolAllocType(uiAction_t, ui_sysPool); uiAction_t* const value = Mem_PoolAllocType(uiAction_t, ui_sysPool); value->d.terminal.d1.constString = Mem_PoolStrDup(UI_GetPath(functionNode), ui_sysPool, 0); value->next = NULL; action->type = EA_LISTENER; action->d.nonTerminal.left = value; /** @todo It is a hack, we should remove that */ action->d.terminal.d2.constData = &functionNode->onClick; action->next = NULL; /* insert the action */ uiAction_t** anchor = &Com_GetValue<uiAction_t*>(node, property); while (*anchor) anchor = &(*anchor)->next; *anchor = action; }
static linkedList_t *LIST_AllocateEntry(void* const data, linkedList_t* const next = 0) { linkedList_t* const e = Mem_PoolAllocType(linkedList_t, com_genericPool); e->data = data; e->next = next; return e; }
static void UI_MaterialEditorNewStage_f (void) { material_t* m; int id; if (Cmd_Argc() < 2) { Com_Printf("Usage: %s <image index>\n", Cmd_Argv(0)); return; } id = atoi(Cmd_Argv(1)); if (id < 0 || id >= r_numImages) { Com_Printf("Given image index (%i) is out of bounds\n", id); return; } m = &R_GetImageAtIndex(id)->material; materialStage_t* const s = Mem_PoolAllocType(materialStage_t, vid_imagePool); m->num_stages++; /* append the stage to the chain */ if (!m->stages) m->stages = s; else { materialStage_t* ss = m->stages; while (ss->next) ss = ss->next; ss->next = s; } UI_MaterialEditorUpdate(R_GetImageAtIndex(id), s); }
/** * @brief Loads and registers a sound file for later use * @param[in] soundFile The name of the soundfile, relative to the sounds dir * @return The index of the loaded sample or 0 * @sa S_LoadSound */ int S_LoadSampleIdx (const char* soundFile) { Mix_Chunk* chunk; char name[MAX_QPATH]; unsigned hash; if (!s_env.initialized) return 0; Com_StripExtension(soundFile, name, sizeof(name)); if (s_sample_t* const sample = S_FindByName(name)) return sample->index; /* make sure the sound is loaded */ chunk = S_LoadSampleChunk(name); if (!chunk) return 0; /* couldn't load the sound's data */ hash = Com_HashKey(name, SAMPLE_HASH_SIZE); s_sample_t* const sample = Mem_PoolAllocType(s_sample_t, cl_soundSysPool); sample->name = Mem_PoolStrDup(name, cl_soundSysPool, 0); sample->chunk = chunk; sample->hashNext = sampleHash[hash]; sampleHash[hash] = sample; sampleIndex[++sampleIndexLast] = sample; sample->index = sampleIndexLast; return sample->index; }
/** * @brief Init or return a cvar * @param[in] var_name The cvar name * @param[in] var_value The standard cvar value (will be set if the cvar doesn't exist) * @param[in] flags CVAR_USERINFO, CVAR_LATCH, CVAR_SERVERINFO, CVAR_ARCHIVE and so on * @param[in] desc This is a short description of the cvar (see console command cvarlist) * @note CVAR_ARCHIVE: Cvar will be saved to config.cfg when game shuts down - and * will be reloaded when game starts up the next time * @note CVAR_LATCH: Latched cvars will be updated at the next map load * @note CVAR_SERVERINFO: This cvar will be send in the server info response strings (server browser) * @note CVAR_NOSET: This cvar can not be set from the commandline * @note CVAR_USERINFO: This cvar will be added to the userinfo string when changed (network synced) * @note CVAR_DEVELOPER: Only changeable if we are in development mode * If the variable already exists, the value will not be set * The flags will be or'ed in if the variable exists. */ cvar_t* Cvar_GetOrCreate (const char* var_name, const char* var_value, int flags, const char* desc) { const unsigned hash = Com_HashKey(var_name, CVAR_HASH_SIZE); if (flags & (CVAR_USERINFO | CVAR_SERVERINFO)) { if (!Cvar_InfoValidate(var_name)) { Com_Printf("invalid info cvar name\n"); return nullptr; } } if (cvar_t* const var = Cvar_FindVar(var_name)) { if (!var->defaultString && (flags & CVAR_CHEAT)) var->defaultString = Mem_PoolStrDup(var_value, com_cvarSysPool, 0); var->flags |= flags; if (desc) { Mem_Free(var->description); var->description = Mem_PoolStrDup(desc, com_cvarSysPool, 0); } return var; } if (!var_value) return nullptr; if (flags & (CVAR_USERINFO | CVAR_SERVERINFO)) { if (!Cvar_InfoValidate(var_value)) { Com_Printf("invalid info cvar value '%s' of cvar '%s'\n", var_value, var_name); return nullptr; } } cvar_t* const var = Mem_PoolAllocType(cvar_t, com_cvarSysPool); var->name = Mem_PoolStrDup(var_name, com_cvarSysPool, 0); var->string = Mem_PoolStrDup(var_value, com_cvarSysPool, 0); var->oldString = nullptr; var->modified = true; var->value = atof(var->string); var->integer = atoi(var->string); if (desc) var->description = Mem_PoolStrDup(desc, com_cvarSysPool, 0); HASH_Add(cvarVarsHash, var, hash); /* link the variable in */ var->next = cvarVars; cvarVars = var; if (var->next) var->next->prev = var; var->flags = flags; if (var->flags & CVAR_CHEAT) var->defaultString = Mem_PoolStrDup(var_value, com_cvarSysPool, 0); for (CvarListeners::iterator i = cvarListeners.begin(); i != cvarListeners.end(); ++i) { (*i)->onCreate(var); } return var; }
/** * @sa NET_DatagramSocketNew */ static struct datagram_socket* NET_DatagramSocketDoNew (const struct addrinfo* addr) { SOCKET sock = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol); int t = 1; const int index = NET_DatagramFindFreeSocket(); if (index == -1) { Com_Printf("Too many datagram sockets open\n"); return nullptr; } if (sock == INVALID_SOCKET) { Com_Printf("Failed to create socket: %s\n", netStringError(netError)); return nullptr; } if (!NET_SocketSetNonBlocking(sock)) { netCloseSocket(sock); return nullptr; } if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char*) &t, sizeof(t)) != 0) { Com_Printf("Failed to set SO_REUSEADDR on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*) &t, sizeof(t)) != 0) { Com_Printf("Failed to set SO_BROADCAST on socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } if (bind(sock, addr->ai_addr, addr->ai_addrlen) != 0) { Com_Printf("Failed to bind socket: %s\n", netStringError(netError)); netCloseSocket(sock); return nullptr; } maxfd = std::max(sock + 1, maxfd); FD_SET(sock, &read_fds); datagram_socket* const s = Mem_PoolAllocType(datagram_socket, com_networkPool); s->family = addr->ai_family; s->addrlen = addr->ai_addrlen; s->socket = sock; s->index = index; s->queue = nullptr; s->queue_tail = &s->queue; s->func = nullptr; datagram_sockets[index] = s; return s; }
/** * @brief Reorders all surfaces arrays for the specified model, grouping the surface * pointers by texture. This dramatically reduces glBindTexture calls. */ static void R_SortSurfacesArrays (const model_t *mod) { const mBspSurface_t *surf, *s; int i, ns; mBspSurfaces_t **r_sorted_surfaces = Mem_AllocTypeN(mBspSurfaces_t *, r_numImages); /* resolve the start surface and total surface count */ s = &mod->bsp.surfaces[mod->bsp.firstmodelsurface]; ns = mod->bsp.nummodelsurfaces; /* allocate the per-texture surfaces arrays and determine counts */ for (i = 0, surf = s; i < ns; i++, surf++) { int index = R_GetImageIndex(surf->texinfo->image); mBspSurfaces_t *surfs = r_sorted_surfaces[index]; if (!surfs) { /* allocate it */ surfs = Mem_PoolAllocType(mBspSurfaces_t, vid_modelPool); r_sorted_surfaces[index] = surfs; } surfs->count++; } /* allocate the surfaces pointers based on counts */ for (i = 0; i < r_numImages; i++) { mBspSurfaces_t *surfs = r_sorted_surfaces[i]; if (surfs) { surfs->surfaces = Mem_PoolAllocTypeN(mBspSurface_t*, surfs->count, vid_modelPool); surfs->count = 0; } } /* sort the model's surfaces arrays into the per-texture arrays */ for (i = 0; i < NUM_SURFACES_ARRAYS; i++) { if (mod->bsp.sorted_surfaces[i]->count) { R_SortSurfacesArrays_(mod->bsp.sorted_surfaces[i], r_sorted_surfaces); Com_DPrintf(DEBUG_RENDERER, "%i: #%i surfaces\n", i, mod->bsp.sorted_surfaces[i]->count); } } /* free the per-texture surfaces arrays */ for (i = 0; i < r_numImages; i++) { mBspSurfaces_t *surfs = r_sorted_surfaces[i]; if (surfs) { Mem_Free(surfs->surfaces); Mem_Free(surfs); } } Mem_Free(r_sorted_surfaces); }
/** * @brief Set a new action to a @c uiAction_t pointer * @param[in,out] action Allocated action * @param[in] type Only @c EA_CMD is supported * @param[in] data The data for this action - in case of @c EA_CMD this is the commandline * @note You first have to free existing node actions - only free those that are * not static in @c ui_global.actions array * @todo we should create a function to free the memory. We can use a tag in the Mem_PoolAlloc * calls and use use Mem_FreeTag. */ void UI_PoolAllocAction (uiAction_t** action, int type, const void *data) { if (*action) Com_Error(ERR_FATAL, "There is already an action assigned"); *action = Mem_PoolAllocType(uiAction_t, ui_sysPool); (*action)->type = type; switch (type) { case EA_CMD: (*action)->d.terminal.d1.constString = Mem_PoolStrDup((const char *)data, ui_sysPool, 0); break; default: Com_Error(ERR_FATAL, "Action type %i is not yet implemented", type); } }
static void R_LoadSurfacesArrays_ (model_t *mod) { mBspSurface_t *surf, *s; int i, ns; /* allocate the surfaces array structures */ /** @todo only one allocation should be used here - allocate the whole array with one Mem_PoolAlloc call */ for (i = 0; i < NUM_SURFACES_ARRAYS; i++) mod->bsp.sorted_surfaces[i] = Mem_PoolAllocType(mBspSurfaces_t, vid_modelPool); /* resolve the start surface and total surface count */ s = &mod->bsp.surfaces[mod->bsp.firstmodelsurface]; ns = mod->bsp.nummodelsurfaces; /* determine the maximum counts for each rendered type in order to * allocate only what is necessary for the specified model */ for (i = 0, surf = s; i < ns; i++, surf++) { const mBspTexInfo_t *texinfo = surf->texinfo; const material_t *material = &texinfo->image->material; if (texinfo->flags & (SURF_BLEND33 | SURF_BLEND66)) { if (texinfo->flags & SURF_WARP) mod->bsp.blend_warp_surfaces->count++; else mod->bsp.blend_surfaces->count++; } else { if (texinfo->flags & SURF_WARP) mod->bsp.opaque_warp_surfaces->count++; else if (texinfo->flags & SURF_ALPHATEST) mod->bsp.alpha_test_surfaces->count++; else mod->bsp.opaque_surfaces->count++; } if (material->flags & STAGE_RENDER) mod->bsp.material_surfaces->count++; if (material->flags & STAGE_FLARE) mod->bsp.flare_surfaces->count++; } /* allocate the surfaces pointers based on the counts */ for (i = 0; i < NUM_SURFACES_ARRAYS; i++) { mBspSurfaces_t *surfaces = mod->bsp.sorted_surfaces[i]; if (surfaces->count) { surfaces->surfaces = Mem_PoolAllocTypeN(mBspSurface_t*, surfaces->count, vid_modelPool); surfaces->count = 0; } }
/** * @brief Append a new mapname to the list of maps for the cycle * @todo check for maps and valid gametypes here * @sa SV_MapcycleClear */ static void SV_MapcycleAdd (const char *mapName, bool day, const char *gameType) { mapcycle_t* const mapcycle = Mem_PoolAllocType(mapcycle_t, sv_genericPool); mapcycle->map = Mem_PoolStrDup(mapName, sv_genericPool, 0); mapcycle->day = day; mapcycle->type = Mem_PoolStrDup(gameType, sv_genericPool, 0); mapcycle->next = 0; Com_DPrintf(DEBUG_SERVER, "mapcycle add: '%s' type '%s'\n", mapcycle->map, mapcycle->type); /* Append to end of list. */ mapcycle_t **anchor = &mapcycleList; while (*anchor) anchor = &(*anchor)->next; *anchor = mapcycle; ++mapcycleCount; }
/** * @sa NET_DatagramSocketNew */ void NET_DatagramSend (struct datagram_socket* s, const char* buf, int len, struct sockaddr* to) { if (!s || len <= 0 || !buf || !to) return; datagram* const dgram = Mem_PoolAllocType(datagram, com_networkPool); dgram->msg = Mem_PoolAllocTypeN(char, len, com_networkPool); dgram->addr = Mem_PoolAllocTypeN(char, s->addrlen, com_networkPool); memcpy(dgram->msg, buf, len); memcpy(dgram->addr, to, len); dgram->len = len; dgram->next = nullptr; *s->queue_tail = dgram; s->queue_tail = &dgram->next; FD_SET(s->socket, &write_fds); }
/** * @brief Add a node to the child index */ bool UI_WindowNodeAddIndexedNode (uiNode_t* const node, uiNode_t* const child) { node_index_t* a; unsigned int hash = Com_HashKey(child->name, INDEXEDCHILD_HASH_SIZE); for (a = EXTRADATA(node).index_hash[hash]; a; a = a->hash_next) { if (Q_streq(child->name, a->node->name)) { /** @todo display a warning, we must not override a node name here */ break; } } if (!a) { a = Mem_PoolAllocType(node_index_t, ui_sysPool); a->next = EXTRADATA(node).index; a->hash_next = EXTRADATA(node).index_hash[hash]; EXTRADATA(node).index_hash[hash] = a; EXTRADATA(node).index = a; } return false; }
/** * @sa NET_StreamGetFree * @sa NET_StreamClose */ static struct net_stream* NET_StreamNew (int index) { net_stream* const s = Mem_PoolAllocType(net_stream, com_networkPool); s->data = nullptr; s->loopback_peer = nullptr; s->loopback = false; s->closed = false; s->finished = false; s->ready = false; s->socket = INVALID_SOCKET; s->inbound = dbufferptr(); s->outbound = dbufferptr(); s->index = index; s->family = 0; s->addrlen = 0; s->func = nullptr; if (streams[index]) NET_StreamFree(streams[index]); streams[index] = s; return s; }
void R_CreateSurfaceFlare (mBspSurface_t* surf) { material_t* m; const materialStage_t* s; vec3_t span; m = &surf->texinfo->image->material; if (!(m->flags & STAGE_FLARE)) /* surface is not flared */ return; surf->flare = Mem_PoolAllocType(mBspFlare_t, vid_modelPool); /* move the flare away from the surface, into the level */ VectorMA(surf->center, 2, surf->normal, surf->flare->origin); /* calculate the flare radius based on surface size */ VectorSubtract(surf->maxs, surf->mins, span); surf->flare->radius = VectorLength(span); s = m->stages; /* resolve the flare stage */ for (;;) { if (s->flags & STAGE_FLARE) break; s = s->next; } /* resolve flare color */ if (s->flags & STAGE_COLOR) VectorCopy(s->color, surf->flare->color); else VectorCopy(surf->lightColor, surf->flare->color); /* and scaled radius */ if (s->flags & (STAGE_SCALE_S | STAGE_SCALE_T)) surf->flare->radius *= (s->scale.s ? s->scale.s : s->scale.t); /* and image */ surf->flare->image = s->image; }
/** * @brief Only called once at startup, not for each game */ void SV_Init (void) { Com_Printf("\n------ server initialization -------\n"); sv_genericPool = Mem_CreatePool("Server: Generic"); OBJZERO(svs); sv = Mem_PoolAllocType(serverInstanceGame_t, sv_genericPool); SV_InitOperatorCommands(); rcon_password = Cvar_Get("rcon_password", "", CVAR_ARCHIVE); Cvar_Get("sv_cheats", "0", CVAR_SERVERINFO | CVAR_LATCH); Cvar_Get("sv_protocol", DOUBLEQUOTE(PROTOCOL_VERSION), CVAR_SERVERINFO | CVAR_NOSET); /* this cvar will become a latched cvar when you start the server */ sv_maxclients = Cvar_Get("sv_maxclients", "1", CVAR_SERVERINFO, "Max. connected clients"); sv_hostname = Cvar_Get("sv_hostname", "noname", CVAR_SERVERINFO | CVAR_ARCHIVE, "The name of the server that is displayed in the serverlist"); sv_http_downloadserver = Cvar_Get("sv_http_downloadserver", "", CVAR_ARCHIVE, "URL to a location where clients can download game content over HTTP"); sv_enablemorale = Cvar_Get("sv_enablemorale", "1", CVAR_ARCHIVE | CVAR_SERVERINFO | CVAR_LATCH, "Enable morale changes in multiplayer"); sv_maxsoldiersperteam = Cvar_Get("sv_maxsoldiersperteam", "4", CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers per team (see sv_maxsoldiersperplayer and sv_teamplay)"); sv_maxsoldiersperplayer = Cvar_Get("sv_maxsoldiersperplayer", STRINGIFY(MAX_ACTIVETEAM), CVAR_ARCHIVE | CVAR_SERVERINFO, "Max. amount of soldiers each player can control (see maxsoldiers and sv_teamplay)"); Cvar_SetCheckFunction("sv_maxsoldiersperplayer", SV_CheckMaxSoldiersPerPlayer); sv_dumpmapassembly = Cvar_Get("sv_dumpmapassembly", "0", CVAR_ARCHIVE, "Dump map assembly information to game console"); sv_threads = Cvar_Get("sv_threads", "1", CVAR_LATCH | CVAR_ARCHIVE, "Run the server threaded"); sv_rma = Cvar_Get("sv_rma", "2", 0, "1 = old algorithm, 2 = new algorithm"); sv_rmadisplaythemap = Cvar_Get("sv_rmadisplaythemap", "0", 0, "Activate rma problem output"); sv_public = Cvar_Get("sv_public", "1", 0, "Should heartbeats be send to the masterserver"); sv_reconnect_limit = Cvar_Get("sv_reconnect_limit", "3", CVAR_ARCHIVE, "Minimum seconds between connect messages"); sv_timeout = Cvar_Get("sv_timeout", "20", CVAR_ARCHIVE, "Seconds until a client times out"); SV_MapcycleInit(); SV_LogInit(); }
/** * @brief Parse all language definitions from the script files */ void CL_ParseLanguages (const char* name, const char** text) { const char* errhead = "CL_ParseLanguages: unexpected end of file (language "; const char* token; if (!*text) { Com_Printf("CL_ParseLanguages: language without body ignored (%s)\n", name); return; } token = Com_EParse(text, errhead, name); if (!*text || *token != '{') { Com_Printf("CL_ParseLanguages: language without body ignored (%s)\n", name); return; } language_t* const language = Mem_PoolAllocType(language_t, cl_genericPool); language->localeID = Mem_PoolStrDup(name, cl_genericPool, 0); language->localeString = ""; language->nativeString = ""; language->localeMapping = nullptr; do { /* get the name type */ token = Com_EParse(text, errhead, name); if (!*text || *token == '}') break; /* inner locale id definition */ if (Q_streq(token, "code")) { linkedList_t* list; if (!Com_ParseList(text, &list)) { Com_Error(ERR_DROP, "CL_ParseLanguages: error while reading language codes \"%s\"", name); } for (linkedList_t* element = list; element != nullptr; element = element->next) { localeMapping_t* const mapping = Mem_PoolAllocType(localeMapping_t, cl_genericPool); mapping->localeMapping = Mem_PoolStrDup((char*)element->data, cl_genericPool, 0); /* link it in */ mapping->next = language->localeMapping; language->localeMapping = mapping; } LIST_Delete(&list); } else if (Q_streq(token, "name")) { token = Com_EParse(text, errhead, name); if (!*text || *token == '}') Com_Error(ERR_FATAL, "CL_ParseLanguages: Name expected for language \"%s\".\n", name); if (*token != '_') { Com_Printf("CL_ParseLanguages: language: '%s' - not marked translatable (%s)\n", name, token); } language->localeString = Mem_PoolStrDup(token, cl_genericPool, 0); } else if (Q_streq(token, "native")) { token = Com_EParse(text, errhead, name); if (!*text || *token == '}') Com_Error(ERR_FATAL, "CL_ParseLanguages: Native expected for language \"%s\".\n", name); language->nativeString = Mem_PoolStrDup(token, cl_genericPool, 0); } } while (*text); language->next = languageList; languageList = language; languageCount++; }
/** * @todo need to merge UI model case, and the common case (looks to be a copy-pasted code) */ void UI_DrawModelNode (uiNode_t* node, const char* source) { modelInfo_t mi; uiModel_t* model; vec3_t nodeorigin; vec2_t screenPos; assert(UI_NodeInstanceOf(node, "model")); /**< We use model extradata */ if (!source || source[0] == '\0') return; model = UI_GetUIModel(source); /* direct model name - no UI model definition */ if (!model) { /* prevent the searching for a model def in the next frame */ mi.model = R_FindModel(source); mi.name = source; if (!mi.model) { Com_Printf("Could not find model '%s'\n", source); return; } } /* compute the absolute origin ('origin' property is relative to the node center) */ UI_GetNodeScreenPos(node, screenPos); UI_GetNodeAbsPos(node, nodeorigin); R_CleanupDepthBuffer(nodeorigin[0], nodeorigin[1], node->box.size[0], node->box.size[1]); if (EXTRADATA(node).clipOverflow) { UI_PushClipRect(screenPos[0], screenPos[1], node->box.size[0], node->box.size[1]); } nodeorigin[0] += node->box.size[0] / 2 + EXTRADATA(node).origin[0]; nodeorigin[1] += node->box.size[1] / 2 + EXTRADATA(node).origin[1]; nodeorigin[2] = EXTRADATA(node).origin[2]; VectorMA(EXTRADATA(node).angles, cls.frametime, EXTRADATA(node).omega, EXTRADATA(node).angles); mi.origin = nodeorigin; mi.angles = EXTRADATA(node).angles; mi.scale = EXTRADATA(node).scale; mi.center = nullVector; mi.color = node->color; mi.mesh = 0; /* special case to draw models with UI model */ if (model) { UI_DrawModelNodeWithUIModel(node, source, &mi, model); if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); return; } /* if the node is linked to a parent, the parent will display it */ if (EXTRADATA(node).tag) { if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); return; } /* autoscale? */ if (EXTRADATA(node).autoscale) { vec3_t autoScale; vec3_t autoCenter; const vec2_t size = {node->box.size[0] - node->padding, node->box.size[1] - node->padding}; R_ModelAutoScale(size, &mi, autoScale, autoCenter); } /* no animation */ mi.frame = 0; mi.oldframe = 0; mi.backlerp = 0; /* get skin */ if (EXTRADATA(node).skin && *EXTRADATA(node).skin) mi.skin = atoi(UI_GetReferenceString(node, EXTRADATA(node).skin)); else mi.skin = 0; /* do animations */ if (EXTRADATA(node).animation && *EXTRADATA(node).animation) { const char* ref; ref = UI_GetReferenceString(node, EXTRADATA(node).animation); /* check whether the cvar value changed */ if (strncmp(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE)) { Q_strncpyz(EXTRADATA(node).oldRefValue, source, MAX_OLDREFVALUE); /* model has changed but mem is already reserved in pool */ Mem_Free(EXTRADATA(node).animationState); EXTRADATA(node).animationState = nullptr; } animState_t* as = EXTRADATA(node).animationState; if (!as) { as = Mem_PoolAllocType(animState_t, cl_genericPool); if (!as) Com_Error(ERR_DROP, "Model %s should have animState_t for animation %s - but doesn't\n", mi.name, ref); R_AnimChange(as, mi.model, ref); EXTRADATA(node).animationState = as; } else { const char* anim; /* change anim if needed */ anim = R_AnimGetName(as, mi.model); if (anim && !Q_streq(anim, ref)) R_AnimChange(as, mi.model, ref); R_AnimRun(as, mi.model, cls.frametime * 1000); } mi.frame = as->frame; mi.oldframe = as->oldframe; mi.backlerp = as->backlerp; } /* draw the main model on the node */ R_DrawModelDirect(&mi, nullptr, nullptr); /* draw all children */ if (node->firstChild) { uiNode_t* child; modelInfo_t pmi = mi; for (child = node->firstChild; child; child = child->next) { const char* tag; char childSource[MAX_VAR]; const char* childRef; /* skip non "model" nodes */ if (child->behaviour != node->behaviour) continue; /* skip invisible child */ if (child->invis || !UI_CheckVisibility(child)) continue; OBJZERO(mi); mi.angles = EXTRADATA(child).angles; mi.scale = EXTRADATA(child).scale; mi.center = nullVector; mi.origin = EXTRADATA(child).origin; mi.color = pmi.color; /* get the anchor name to link the model into the parent */ tag = EXTRADATA(child).tag; /* init model name */ childRef = UI_GetReferenceString(child, EXTRADATA(child).model); if (Q_strnull(childRef)) childSource[0] = '\0'; else Q_strncpyz(childSource, childRef, sizeof(childSource)); mi.model = R_FindModel(childSource); mi.name = childSource; /* init skin */ if (EXTRADATA(child).skin && *EXTRADATA(child).skin) mi.skin = atoi(UI_GetReferenceString(child, EXTRADATA(child).skin)); else mi.skin = 0; R_DrawModelDirect(&mi, &pmi, tag); } } if (EXTRADATA(node).clipOverflow) UI_PopClipRect(); }
/** * @brief Load material definitions for each map that has one * @param[in] map the base name of the map to load the material for */ void R_LoadMaterials (const char *map) { char path[MAX_QPATH]; byte *fileBuffer; const char *buffer; bool inmaterial; image_t *image; material_t *m; materialStage_t *ss; /* clear previously loaded materials */ R_ImageClearMaterials(); if (map[0] == '+' || map[0] == '-') map++; else if (map[0] == '-') return; /* load the materials file for parsing */ Com_sprintf(path, sizeof(path), "materials/%s.mat", Com_SkipPath(map)); if (FS_LoadFile(path, &fileBuffer) < 1) { Com_DPrintf(DEBUG_RENDERER, "Couldn't load %s\n", path); return; } else { Com_Printf("load material file: '%s'\n", path); if (!r_materials->integer) Com_Printf("...ignore materials (r_materials is deactivated)\n"); } buffer = (const char *)fileBuffer; inmaterial = false; image = nullptr; m = nullptr; while (true) { const char *c = Com_Parse(&buffer); if (c[0] == '\0') break; if (*c == '{' && !inmaterial) { inmaterial = true; continue; } if (Q_streq(c, "material")) { c = Com_Parse(&buffer); image = R_GetImage(va("textures/%s", c)); if (image == nullptr) Com_DPrintf(DEBUG_RENDERER, "R_LoadMaterials: skip texture: %s - not used in the map\n", c); continue; } if (!image) continue; m = &image->material; if (Q_streq(c, "normalmap")){ c = Com_Parse(&buffer); image->normalmap = R_FindImage(va("textures/%s", c), it_normalmap); if (image->normalmap == r_noTexture){ Com_Printf("R_LoadMaterials: Failed to resolve normalmap: %s\n", c); image->normalmap = nullptr; } } if (Q_streq(c, "glowmap")){ c = Com_Parse(&buffer); image->glowmap = R_FindImage(va("textures/%s", c), it_glowmap); if (image->glowmap == r_noTexture){ Com_Printf("R_LoadMaterials: Failed to resolve glowmap: %s\n", c); image->glowmap = nullptr; } } if (Q_streq(c, "bump")) { m->bump = atof(Com_Parse(&buffer)); if (m->bump < 0.0) { Com_Printf("R_LoadMaterials: Invalid bump value for %s\n", image->name); m->bump = defaultMaterial.bump; } } if (Q_streq(c, "parallax")) { m->parallax = atof(Com_Parse(&buffer)); if (m->parallax < 0.0) { Com_Printf("R_LoadMaterials: Invalid parallax value for %s\n", image->name); m->parallax = defaultMaterial.parallax; } } if (Q_streq(c, "hardness")) { m->hardness = atof(Com_Parse(&buffer)); if (m->hardness < 0.0) { Com_Printf("R_LoadMaterials: Invalid hardness value for %s\n", image->name); m->hardness = defaultMaterial.hardness; } } if (Q_streq(c, "specular")) { m->specular = atof(Com_Parse(&buffer)); if (m->specular < 0.0) { Com_Printf("R_LoadMaterials: Invalid specular value for %s\n", image->name); m->specular = defaultMaterial.specular; } } if (Q_streq(c, "glowscale")) { m->glowscale = atof(Com_Parse(&buffer)); if (m->glowscale < 0.0) { Com_Printf("R_LoadMaterials: Invalid glowscale value for %s\n", image->name); m->glowscale = defaultMaterial.glowscale; } } if (*c == '{' && inmaterial) { materialStage_t* const s = Mem_PoolAllocType(materialStage_t, vid_imagePool); s->glowscale = defaultMaterial.glowscale; if (R_ParseStage(s, &buffer) == -1) { Mem_Free(s); continue; } /* load animation frame images */ if (s->flags & STAGE_ANIM) { if (R_LoadAnimImages(s) == -1) { Mem_Free(s); continue; } } /* append the stage to the chain */ if (!m->stages) m->stages = s; else { ss = m->stages; while (ss->next) ss = ss->next; ss->next = s; } m->flags |= s->flags; m->num_stages++; continue; } if (*c == '}' && inmaterial) { Com_DPrintf(DEBUG_RENDERER, "Parsed material %s with %d stages\n", image->name, m->num_stages); inmaterial = false; image = nullptr; /* multiply stage glowscale values by material glowscale */ ss = m->stages; while (ss) { ss->glowscale *= m->glowscale; ss = ss->next; } } } FS_FreeFile(fileBuffer); R_CreateMaterialData(); }
/** * @brief Adds a new message to message stack * @note These are the messages that are displayed at geoscape * @param[in] title Already translated message/mail title * @param[in] text Already translated message/mail body * @param[in] popup Show this as a popup, too? * @param[in] type The message type * @param[in] pedia Pointer to technology (only if needed) * @param[in] playSound Whether the sound associated with the message type should be played * @return message_t pointer * @sa UP_OpenMail_f * @sa CL_EventAddMail_f * @note this method forwards to @c MS_AddNewMessageSound with @code playSound = true @endcode */ uiMessageListNodeMessage_t* MS_AddNewMessage (const char* title, const char* text, messageType_t type, technology_t* pedia, bool popup, bool playSound) { assert(type < MSG_MAX); /* allocate memory for new message - delete this with every new game */ uiMessageListNodeMessage_t* const mess = Mem_PoolAllocType(uiMessageListNodeMessage_t, cp_campaignPool); switch (type) { case MSG_DEBUG: /**< only save them in debug mode */ mess->iconName = "icons/message_debug"; break; case MSG_INFO: /**< don't save these messages */ mess->iconName = "icons/message_info"; break; case MSG_STANDARD: mess->iconName = "icons/message_info"; break; case MSG_RESEARCH_PROPOSAL: mess->iconName = "icons/message_research"; break; case MSG_RESEARCH_HALTED: mess->iconName = "icons/message_research"; break; case MSG_RESEARCH_FINISHED: mess->iconName = "icons/message_research"; break; case MSG_CONSTRUCTION: mess->iconName = "icons/message_construction"; break; case MSG_UFOSPOTTED: mess->iconName = "icons/message_ufo"; break; case MSG_TERRORSITE: mess->iconName = "icons/message_ufo"; break; case MSG_BASEATTACK: mess->iconName = "icons/message_ufo"; break; case MSG_TRANSFERFINISHED: mess->iconName = "icons/message_transfer"; break; case MSG_PROMOTION: mess->iconName = "icons/message_promotion"; break; case MSG_PRODUCTION: mess->iconName = "icons/message_production"; break; case MSG_DEATH: mess->iconName = "icons/message_death"; break; case MSG_CRASHSITE: mess->iconName = "icons/message_ufo"; break; case MSG_EVENT: mess->iconName = "icons/message_info"; break; default: mess->iconName = "icons/message_info"; break; } /* push the new message at the beginning of the stack */ cgi->UI_MessageAddStack(mess); mess->type = type; mess->pedia = pedia; /* pointer to UFOpaedia entry */ mess->date = ccs.date; Q_strncpyz(mess->title, title, sizeof(mess->title)); mess->text = cgi->PoolStrDup(text, cp_campaignPool, 0); /* get formatted date text */ MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp)); /* they need to be translated already */ if (popup) { CP_GameTimeStop(); CP_Popup(mess->title, "%s", mess->text); } if (playSound) { const char* sound = nullptr; switch (type) { case MSG_DEBUG: break; case MSG_STANDARD: sound = "geoscape/standard"; break; case MSG_INFO: case MSG_TRANSFERFINISHED: case MSG_DEATH: case MSG_CONSTRUCTION: case MSG_PRODUCTION: sound = "geoscape/info"; break; case MSG_RESEARCH_PROPOSAL: case MSG_RESEARCH_FINISHED: assert(pedia); case MSG_RESEARCH_HALTED: case MSG_EVENT: case MSG_NEWS: /* reread the new mails in UP_GetUnreadMails */ ccs.numUnreadMails = -1; sound = "geoscape/mail"; break; case MSG_UFOLOST: sound = "geoscape/ufolost"; break; case MSG_UFOSPOTTED: sound = "geoscape/ufospotted"; break; case MSG_BASEATTACK: sound = "geoscape/basealert"; break; case MSG_TERRORSITE: sound = "geoscape/alien-activity"; break; case MSG_CRASHSITE: sound = "geoscape/newmission"; break; case MSG_PROMOTION: sound = "geoscape/promotion"; break; case MSG_MAX: break; } cgi->S_StartLocalSample(sound, 1.0f); } return mess; }
static cvarChangeListener_t* Cvar_GetChangeListener (cvarChangeListenerFunc_t listenerFunc) { cvarChangeListener_t* const listener = Mem_PoolAllocType(cvarChangeListener_t, com_cvarSysPool); listener->exec = listenerFunc; return listener; }