Ejemplo n.º 1
0
/**
 * @brief Returns formatted text of a message timestamp
 * @param[in] text Buffer to hold the final result
 * @param[in] message The message to convert into text
 * @param[in] textsize The maximum length for text
 */
static void MS_TimestampedText (char* text, uiMessageListNodeMessage_t* message, size_t textsize)
{
	dateLong_t date;
	CP_DateConvertLong(&message->date, &date);
	Com_sprintf(text, textsize, _("%i %s %02i, %02i:%02i: "), date.year,
		Date_GetMonthName(date.month - 1), date.day, date.hour, date.min);
}
Ejemplo n.º 2
0
/**
 * @brief Updates date/time and timescale (=timelapse) on the geoscape menu
 * @sa SAV_GameLoad
 * @sa CP_CampaignRun
 */
void CP_UpdateTime (void)
{
	dateLong_t date;
	CP_DateConvertLong(&ccs.date, &date);

	/* Update the timelapse text */
	if (ccs.gameLapse >= 0 && ccs.gameLapse < NUM_TIMELAPSE) {
		cgi->Cvar_Set("mn_timelapse", "%s", _(lapse[ccs.gameLapse].name));
		ccs.gameTimeScale = lapse[ccs.gameLapse].scale;
		cgi->Cvar_SetValue("mn_timelapse_id", ccs.gameLapse);
	}

	/* Update the date */
	cgi->Cvar_Set("mn_mapdate", _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day);

	/* Update the time. */
	cgi->Cvar_Set("mn_maptime", _("%02i:%02i"), date.hour, date.min);
}
Ejemplo n.º 3
0
/**
 * @brief Adds the event mail to the message stack. This message is going to be added to the savegame.
 */
void CL_EventAddMail (const char *eventMailId)
{
	eventMail_t* eventMail = CL_GetEventMail(eventMailId);
	if (!eventMail) {
		Com_Printf("CL_EventAddMail: Could not find eventmail with id '%s'\n", eventMailId);
		return;
	}

	if (eventMail->sent) {
		return;
	}

	if (!eventMail->from || !eventMail->to || !eventMail->subject || !eventMail->body) {
		Com_Printf("CL_EventAddMail: mail with id '%s' has incomplete data\n", eventMailId);
		return;
	}

	if (!eventMail->date) {
		dateLong_t date;
		char dateBuf[MAX_VAR] = "";

		CP_DateConvertLong(&ccs.date, &date);
		Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
			date.year, Date_GetMonthName(date.month - 1), date.day);
		eventMail->date = Mem_PoolStrDup(dateBuf, cp_campaignPool, 0);
	}

	eventMail->sent = true;

	if (!eventMail->skipMessage) {
		uiMessageListNodeMessage_t *m = MS_AddNewMessage("", va(_("You've got a new mail: %s"), _(eventMail->subject)), MSG_EVENT);
		if (m)
			m->eventMail = eventMail;
		else
			Com_Printf("CL_EventAddMail: Could not add message with id: %s\n", eventMailId);
	}

	UP_OpenEventMail(eventMailId);
}
Ejemplo n.º 4
0
/**
 * @brief Binds the mail header (if needed) to the mn.menuText array.
 * @note if there is a mail header.
 * @param[in] tech The tech to generate a header for.
 * @param[in] type The type of mail (research proposal or finished research)
 * @param[in] mail The mail descriptor structure
 * @sa UP_ChangeDisplay
 * @sa CL_EventAddMail_f
 * @sa CL_GetEventMail
 */
static void UP_SetMailHeader (technology_t* tech, techMailType_t type, eventMail_t* mail)
{
	static char mailHeader[8 * MAX_VAR] = ""; /* bigger as techMail_t (utf8) */
	char dateBuf[MAX_VAR] = "";
	const char* subjectType = "";
	const char* from, *to, *subject, *model;
	dateLong_t date;

	if (mail) {
		from = mail->from;
		to = mail->to;
		model = mail->model;
		subject = mail->subject;
		Q_strncpyz(dateBuf, _(mail->date), sizeof(dateBuf));
		mail->read = true;
		/* reread the unread mails in UP_GetUnreadMails */
		ccs.numUnreadMails = -1;
	} else {
		techMail_t* mail;
		assert(tech);
		assert(type < TECHMAIL_MAX);

		mail = &tech->mail[type];
		from = mail->from;
		to = mail->to;
		subject = mail->subject;
		model = mail->model;

		if (mail->date) {
			Q_strncpyz(dateBuf, _(mail->date), sizeof(dateBuf));
		} else {
			switch (type) {
			case TECHMAIL_PRE:
				CP_DateConvertLong(&tech->preResearchedDate, &date);
				Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
					date.year, Date_GetMonthName(date.month - 1), date.day);
				break;
			case TECHMAIL_RESEARCHED:
				CP_DateConvertLong(&tech->researchedDate, &date);
				Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
					date.year, Date_GetMonthName(date.month - 1), date.day);
				break;
			default:
				cgi->Com_Error(ERR_DROP, "UP_SetMailHeader: unhandled techMailType_t %i for date.", type);
			}
		}
		if (from != nullptr) {
			if (!mail->read) {
				mail->read = true;
				/* reread the unread mails in UP_GetUnreadMails */
				ccs.numUnreadMails = -1;
			}
			/* only if mail and mail_pre are available */
			if (tech->numTechMails == TECHMAIL_MAX) {
				switch (type) {
				case TECHMAIL_PRE:
					subjectType = _("Proposal: ");
					break;
				case TECHMAIL_RESEARCHED:
					subjectType = _("Re: ");
					break;
				default:
					cgi->Com_Error(ERR_DROP, "UP_SetMailHeader: unhandled techMailType_t %i for subject.", type);
				}
			}
		} else {
			cgi->UI_ResetData(TEXT_UFOPEDIA_MAILHEADER);
			return;
		}
	}
	from = cgi->CL_Translate(from);
	to = cgi->CL_Translate(to);
	subject = cgi->CL_Translate(subject);
	Com_sprintf(mailHeader, sizeof(mailHeader), _("FROM: %s\nTO: %s\nDATE: %s"), from, to, dateBuf);
	cgi->Cvar_Set("mn_mail_sender_head", "%s", model ? model : "");
	cgi->Cvar_Set("mn_mail_from", "%s", from);
	cgi->Cvar_Set("mn_mail_subject", "%s%s", subjectType, _(subject));
	cgi->Cvar_Set("mn_mail_to", "%s", to);
	cgi->Cvar_Set("mn_mail_date", "%s", dateBuf);
	cgi->UI_RegisterText(TEXT_UFOPEDIA_MAILHEADER, mailHeader);
}
Ejemplo n.º 5
0
/**
 * @brief Start the mailclient
 * @sa UP_MailClientClick_f
 * @sa CP_GetUnreadMails
 * @sa CL_EventAddMail_f
 * @sa MS_AddNewMessage
 */
static void UP_OpenMail_f (void)
{
	cgi->UI_ExecuteConfunc("clear_mails");
	int idx = 0;
	for (const uiMessageListNodeMessage_t* m = cgi->UI_MessageGetStack(); m; m = m->next) {
		dateLong_t date;
		char headline[256] = "";
		char dateBuf[64] = "";
		const char* icon;
		bool read;
		switch (m->type) {
		case MSG_RESEARCH_PROPOSAL: {
			const techMail_t& mail = m->pedia->mail[TECHMAIL_PRE];
			if (!mail.from)
				continue;
			CP_DateConvertLong(&m->pedia->preResearchedDate, &date);
			Com_sprintf(headline, sizeof(headline), _("Proposal: %s"), _(m->pedia->mail[TECHMAIL_PRE].subject));
			Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day);
			icon = mail.icon;
			read = mail.read;
			break;
		}
		case MSG_RESEARCH_FINISHED: {
			const techMail_t& mail = m->pedia->mail[TECHMAIL_RESEARCHED];
			if (!mail.from)
				continue;
			CP_DateConvertLong(&m->pedia->researchedDate, &date);
			Com_sprintf(headline, sizeof(headline), _("Re: %s"), _(m->pedia->mail[TECHMAIL_PRE].subject));
			Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day);
			icon = mail.icon;
			read = mail.read;
			break;
		}
		case MSG_NEWS: {
			const techMail_t* mail = &m->pedia->mail[TECHMAIL_PRE];
			if (mail->from) {
				CP_DateConvertLong(&m->pedia->preResearchedDate, &date);
			} else {
				CP_DateConvertLong(&m->pedia->researchedDate, &date);
				mail = &m->pedia->mail[TECHMAIL_RESEARCHED];
			}
			if (!mail->from)
				continue;
			Com_sprintf(headline, sizeof(headline), "%s", _(mail->subject));
			Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"), date.year, Date_GetMonthName(date.month - 1), date.day);
			icon = mail->icon;
			read = mail->read;
			break;
		}
		case MSG_EVENT: {
			assert(m->eventMail);
			const eventMail_t& mail = *m->eventMail;
			if (!mail.from)
				continue;
			Com_sprintf(headline, sizeof(headline), "%s", _(mail.subject));
			Com_sprintf(dateBuf, sizeof(dateBuf), "%s", mail.date);
			icon = mail.icon;
			read = mail.read;
			break;
		}
		default:
			continue;
		}
		cgi->UI_ExecuteConfunc("add_mail %i \"%s\" \"%s\" %i \"%s\"", idx++, headline, icon, read, dateBuf);
	}
}
Ejemplo n.º 6
0
/**
 * @brief This is a savegame function which stores the game in xml-Format.
 * @param[in] filename The Filename to save to (without extension)
 * @param[in] comment Description of the savegame
 * @param[out] error On failure an errormessage may be set.
 */
static bool SAV_GameSave (const char *filename, const char *comment, char **error)
{
	xmlNode_t *topNode, *node;
	char savegame[MAX_OSPATH];
	int res;
	int requiredBufferLength;
	uLongf bufLen;
	saveFileHeader_t header;
	char dummy[2];
	int i;
	dateLong_t date;
	char message[30];
	char timeStampBuffer[32];

	if (!CP_IsRunning()) {
		*error = _("No campaign active.");
		Com_Printf("Error: No campaign active.\n");
		return false;
	}

	if (!B_AtLeastOneExists()) {
		*error = _("Nothing to save yet.");
		Com_Printf("Error: Nothing to save yet.\n");
		return false;
	}

	Com_MakeTimestamp(timeStampBuffer, sizeof(timeStampBuffer));
	Com_sprintf(savegame, sizeof(savegame), "save/%s.%s", filename, SAVEGAME_EXTENSION);
	topNode = mxmlNewXML("1.0");
	node = XML_AddNode(topNode, SAVE_ROOTNODE);
	/* writing  Header */
	XML_AddInt(node, SAVE_SAVEVERSION, SAVE_FILE_VERSION);
	XML_AddString(node, SAVE_COMMENT, comment);
	XML_AddString(node, SAVE_UFOVERSION, UFO_VERSION);
	XML_AddString(node, SAVE_REALDATE, timeStampBuffer);
	CP_DateConvertLong(&ccs.date, &date);
	Com_sprintf(message, sizeof(message), _("%i %s %02i"),
		date.year, Date_GetMonthName(date.month - 1), date.day);
	XML_AddString(node, SAVE_GAMEDATE, message);
	/* working through all subsystems. perhaps we should redesign it, order is not important anymore */
	Com_Printf("Calling subsystems\n");
	for (i = 0; i < saveSubsystemsAmount; i++) {
		if (!saveSubsystems[i].save(node))
			Com_Printf("...subsystem '%s' failed to save the data\n", saveSubsystems[i].name);
		else
			Com_Printf("...subsystem '%s' - saved\n", saveSubsystems[i].name);
	}

	/* calculate the needed buffer size */
	OBJZERO(header);
	header.compressed = LittleLong(save_compressed->integer);
	header.version = LittleLong(SAVE_FILE_VERSION);
	header.subsystems = LittleLong(saveSubsystemsAmount);
	Q_strncpyz(header.name, comment, sizeof(header.name));
	Q_strncpyz(header.gameVersion, UFO_VERSION, sizeof(header.gameVersion));
	CP_DateConvertLong(&ccs.date, &date);
	Com_sprintf(header.gameDate, sizeof(header.gameDate), _("%i %s %02i"),
		date.year, Date_GetMonthName(date.month - 1), date.day);
	Q_strncpyz(header.realDate, timeStampBuffer, sizeof(header.realDate));

	requiredBufferLength = mxmlSaveString(topNode, dummy, 2, MXML_NO_CALLBACK);

	header.xmlSize = LittleLong(requiredBufferLength);
	byte* const buf = Mem_PoolAllocTypeN(byte, requiredBufferLength + 1, cp_campaignPool);
	if (!buf) {
		mxmlDelete(topNode);
		*error = _("Could not allocate enough memory to save this game");
		Com_Printf("Error: Could not allocate enough memory to save this game\n");
		return false;
	}
	res = mxmlSaveString(topNode, (char*)buf, requiredBufferLength + 1, MXML_NO_CALLBACK);
	mxmlDelete(topNode);
	Com_Printf("XML Written to buffer (%d Bytes)\n", res);

	if (header.compressed)
		bufLen = compressBound(requiredBufferLength);
	else
		bufLen = requiredBufferLength;

	byte* const fbuf = Mem_PoolAllocTypeN(byte, bufLen + sizeof(header), cp_campaignPool);
	memcpy(fbuf, &header, sizeof(header));

	if (header.compressed) {
		res = compress(fbuf + sizeof(header), &bufLen, buf, requiredBufferLength);
		Mem_Free(buf);

		if (res != Z_OK) {
			Mem_Free(fbuf);
			*error = _("Memory error compressing save-game data - set save_compressed cvar to 0");
			Com_Printf("Memory error compressing save-game data (%s) (Error: %i)!\n", comment, res);
			return false;
		}
	} else {
		memcpy(fbuf + sizeof(header), buf, requiredBufferLength);
		Mem_Free(buf);
	}

	/* last step - write data */
	res = FS_WriteFile(fbuf, bufLen + sizeof(header), savegame);
	Mem_Free(fbuf);

	return true;
}
Ejemplo n.º 7
0
/**
 * @brief Load callback for messages
 * @param[in] p XML Node structure, where we get the information from
 * @sa MS_SaveXML
 * @sa UI_AddNewMessageSound
 */
bool MS_LoadXML (xmlNode_t* p)
{
	int i;
	xmlNode_t* n, *sn;
	n = cgi->XML_GetNode(p, SAVE_MESSAGES_MESSAGES);

	if (!n)
		return false;

	/* we have to set this a little bit higher here, otherwise the samples that are played when adding
	 * a message to the stack would all played a few milliseconds after each other - that doesn't sound
	 * nice */
	cgi->S_SetSampleRepeatRate(500);

	cgi->Com_RegisterConstList(saveMessageConstants);
	for (sn = cgi->XML_GetNode(n, SAVE_MESSAGES_MESSAGE), i = 0; sn; sn = cgi->XML_GetNextNode(sn, n, SAVE_MESSAGES_MESSAGE), i++) {
		eventMail_t* mail;
		const char* type = cgi->XML_GetString(sn, SAVE_MESSAGES_TYPE);
		int mtype;
		char title[MAX_VAR];
		char text[MAX_MESSAGE_TEXT];
		char id[MAX_VAR];
		technology_t* tech = nullptr;
		uiMessageListNodeMessage_t* mess;

		if (!cgi->Com_GetConstIntFromNamespace(SAVE_MESSAGETYPE_NAMESPACE, type, (int*) &mtype)) {
			cgi->Com_Printf("Invalid message type '%s'\n", type);
			continue;
		}

		/* can contain high bits due to utf8 */
		Q_strncpyz(title, cgi->XML_GetString(sn, SAVE_MESSAGES_TITLE), sizeof(title));
		Q_strncpyz(text,  cgi->XML_GetString(sn, SAVE_MESSAGES_TEXT),  sizeof(text));

		if (mtype == MSG_EVENT) {
			mail = CL_GetEventMail(cgi->XML_GetString(sn, SAVE_MESSAGES_EVENTMAILID));
			if (mail)
				mail->read = cgi->XML_GetBool(sn, SAVE_MESSAGES_EVENTMAILREAD, false);
		} else
			mail = nullptr;

		/* event and not mail means, dynamic mail - we don't save or load them */
		if (mtype == MSG_EVENT && !mail)
			continue;
		if (mtype == MSG_DEBUG && cgi->Cvar_GetInteger("developer") == 0)
			continue;

		Q_strncpyz(id, cgi->XML_GetString(sn, SAVE_MESSAGES_PEDIAID), sizeof(id));
		if (id[0] != '\0')
			tech = RS_GetTechByID(id);
		if (!tech && (mtype == MSG_RESEARCH_PROPOSAL || mtype == MSG_RESEARCH_FINISHED)) {
			/** No tech found drop message. */
			continue;
		}
		mess = MS_AddNewMessage(title, text, (messageType_t)mtype, tech, false, false);
		mess->eventMail = mail;
		cgi->XML_GetDate(sn, SAVE_MESSAGES_DATE, &mess->date.day, &mess->date.sec);
		/* redo timestamp text after setting date */
		MS_TimestampedText(mess->timestamp, mess, sizeof(mess->timestamp));

		if (mail) {
			dateLong_t date;
			char dateBuf[MAX_VAR] = "";

			CP_DateConvertLong(&mess->date, &date);
			Com_sprintf(dateBuf, sizeof(dateBuf), _("%i %s %02i"),
				date.year, Date_GetMonthName(date.month - 1), date.day);
			mail->date = cgi->PoolStrDup(dateBuf, cp_campaignPool, 0);
		}
	}
	cgi->Com_UnregisterConstList(saveMessageConstants);

	/* reset the sample repeat rate */
	cgi->S_SetSampleRepeatRate(0);

	return true;
}