1 // -*- Mode: C++; tab-width:2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 // vi:tw=80:et:ts=2:sts=2
3 //
4 // -----------------------------------------------------------------------
5 //
6 // This file is part of RLVM, a RealLive virtual machine clone.
7 //
8 // -----------------------------------------------------------------------
9 //
10 // Copyright (C) 2013 Elliot Glaysher
11 //
12 // This program is free software; you can redistribute it and/or modify
13 // it under the terms of the GNU General Public License as published by
14 // the Free Software Foundation; either version 3 of the License, or
15 // (at your option) any later version.
16 //
17 // This program is distributed in the hope that it will be useful,
18 // but WITHOUT ANY WARRANTY; without even the implied warranty of
19 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 // GNU General Public License for more details.
21 //
22 // You should have received a copy of the GNU General Public License
23 // along with this program; if not, write to the Free Software
24 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 // -----------------------------------------------------------------------
26
27 #include "modules/module_sys_timetable2.h"
28
29 #include <cmath>
30
31 #include "machine/rlmachine.h"
32 #include "machine/rlmodule.h"
33 #include "utilities/exception.h"
34 #include "utilities/math_util.h"
35
36 // static
GetTypeForTag(const libreallive::ExpressionPiece & sp)37 int TimeTableMapper::GetTypeForTag(const libreallive::ExpressionPiece& sp) {
38 switch (sp.GetOverloadTag()) {
39 case 48:
40 return 0;
41 case 65584:
42 return 1;
43 case 49:
44 return 2;
45 case 50:
46 return 3;
47 case 51:
48 return 4;
49 case 52:
50 return 5;
51 case 53:
52 return 6;
53 case 54:
54 return 7;
55 case 55:
56 return 8;
57 default: {
58 std::ostringstream oss;
59 oss << "Invalid timetable2 tag: " << sp.GetOverloadTag();
60 throw rlvm::Exception(oss.str());
61 }
62 }
63 }
64
65 // -----------------------------------------------------------------------
66
operator ()(RLMachine & machine,int now_time,int rep_time,int start_time,int start_num,TimeTable2List::type index_list)67 int Sys_timetable2::operator()(RLMachine& machine,
68 int now_time,
69 int rep_time,
70 int start_time,
71 int start_num,
72 TimeTable2List::type index_list) {
73 now_time = now_time + rep_time;
74
75 if (start_time > now_time)
76 return start_num;
77
78 int value = start_num;
79
80 for (TimeTable2List::type::iterator it = index_list.begin();
81 it != index_list.end();
82 ++it) {
83 switch (it->type) {
84 case 0: {
85 int end_time = std::get<0>(it->first);
86 int end_num = std::get<1>(it->first);
87 if (now_time > start_time && now_time <= end_time) {
88 return InterpolateBetween(start_time, now_time, end_time,
89 value, end_num, 0);
90 } else {
91 value = end_num;
92 }
93
94 start_time = end_time;
95 break;
96 }
97 case 1: {
98 int end_time = std::get<0>(it->second);
99 int end_num = std::get<1>(it->second);
100 int mod = std::get<2>(it->second);
101
102 if (now_time > start_time && now_time <= end_time) {
103 return InterpolateBetween(start_time, now_time, end_time,
104 value, end_num, mod);
105 } else {
106 value = end_num;
107 }
108
109 start_time = end_time;
110 break;
111 }
112 case 2: {
113 value = it->third;
114 break;
115 }
116 case 3: {
117 int end_time = it->fourth;
118 if (now_time > start_time && now_time <= end_time) {
119 return value;
120 }
121
122 start_time = end_time;
123 break;
124 }
125 case 7: {
126 int end_time = std::get<0>(it->eighth);
127 int end_num = std::get<1>(it->eighth);
128 if (now_time > start_time && now_time <= end_time) {
129 int count = std::get<2>(it->eighth);
130 return Jump(start_time, now_time, end_time, value, end_num, count);
131 } else {
132 value = end_num;
133 }
134
135 // Finally, set the start time to our end time.
136 start_time = end_time;
137 break;
138 }
139 case 8: {
140 int end_time = std::get<0>(it->ninth);
141 int end_num = std::get<1>(it->ninth);
142
143 if (now_time > start_time && now_time <= end_time) {
144 return value;
145 } else {
146 value = end_num;
147 }
148
149 start_time = end_time;
150 break;
151 }
152 default: {
153 std::ostringstream oss;
154 oss << "We don't handle " << it->type << " yet.";
155 throw rlvm::Exception(oss.str());
156 }
157 }
158 }
159
160 return value;
161 }
162
Jump(int start_time,int now_time,int end_time,int start_num,int end_num,int count)163 int Sys_timetable2::Jump(int start_time,
164 int now_time,
165 int end_time,
166 int start_num,
167 int end_num,
168 int count) {
169 int duration = end_time - start_time;
170 int one_cycle = duration / count;
171
172 int current_time = now_time - start_time;
173 int current_cycle = current_time % one_cycle;
174
175 double percent = static_cast<double>(current_cycle) / one_cycle;
176 return start_num +
177 ((end_num - start_num) * (1 - std::pow(percent * 2 - 1, 2)));
178 }
179
180 // -----------------------------------------------------------------------
181
182 struct Sys_timetablelen2 : public Sys_timetable2 {
operator ()Sys_timetablelen2183 virtual int operator()(RLMachine& machine,
184 int now_time,
185 int rep_time,
186 int start_time,
187 int start_num,
188 TimeTable2List::type index_list) {
189 // Modify each part of the index list to keep a running sum of times. This
190 // changes a list of durations into a list of absolute times.
191 int total = 0;
192
193 for (TimeTable2List::type::iterator it = index_list.begin();
194 it != index_list.end();
195 ++it) {
196 switch (it->type) {
197 case 0: {
198 total += std::get<0>(it->first);
199 std::get<0>(it->first) = total;
200 break;
201 }
202 case 1: {
203 total += std::get<0>(it->second);
204 std::get<0>(it->second) = total;
205 break;
206 }
207 case 2: {
208 // Set has no time, therefore it isn't modified.
209 break;
210 }
211 case 3: {
212 total += it->fourth;
213 it->fourth = total;
214 break;
215 }
216 case 7: {
217 total += std::get<0>(it->eighth);
218 std::get<0>(it->eighth) = total;
219 break;
220 }
221 case 8: {
222 total += std::get<0>(it->ninth);
223 std::get<0>(it->ninth) = total;
224 break;
225 }
226 default: {
227 std::ostringstream oss;
228 oss << "We don't handle " << it->type << " yet.";
229 throw rlvm::Exception(oss.str());
230 }
231 }
232 }
233
234 return Sys_timetable2::operator()(
235 machine, now_time, rep_time, start_time, start_num, index_list);
236 }
237 };
238
239 // -----------------------------------------------------------------------
240
AddTimetable2Opcode(RLModule & module)241 void AddTimetable2Opcode(RLModule& module) {
242 module.AddOpcode(810, 0, "timetable2", new Sys_timetable2);
243 module.AddOpcode(811, 0, "timetablelen2", new Sys_timetablelen2);
244 }
245